I have a class called Vara, where i have a table field called searchname. I want to do a simple setup of cviebrock eloquent sluggable but can't figure out what the issue is.
When i save my model, nothing happens, it rewrite the old value stored.
If i change in build_from to, whatthefuckisgoingon i get the same output. I have a field called handle, also tried changing the field namne to slug but same result. If i leave build_from empty i also get the same output.
If i however change save_to to something that doesn't exist i get an error. The searchname field does have a value of "Hjordnära test 33 liter", so the output is really wierd.
My guess is that build_from is being ignored, and seen as null. How do i fix this?
My Vara.php looks like this
use Cviebrock\EloquentSluggable\SluggableInterface;
use Cviebrock\EloquentSluggable\SluggableTrait;
class Vara extends \Eloquent implements SluggableInterface {
use SluggableTrait;
protected $sluggable = array(
'build_from' => 'searchname',
'save_to' => 'handle'
);
In my VarorController.php
public function saveVara()
{
$id = Input::get('id');
$vara = Vara::find(Input::get('id'));
$vara->edited_by = Auth::user()->id;
$vara->searchname = Input::get('searchname');
$vara->save();
return $vara->getSlug();
Ok a litle update, found this function in SluggableTrait.php
public function sluggify($force=false)
{
$config = \App::make('config')->get('eloquent-sluggable::config');
$this->sluggable = array_merge( $config, $this->sluggable );
if ($force || $this->needsSlugging())
{
$source = $this->getSlugSource();
$slug = $this->generateSlug($source);
$slug = $this->validateSlug($slug);
$slug = $this->makeSlugUnique($slug);
$this->setSlug($slug);
}
return $this;
}
so if i add $vara->sluggify(true); to my controller the slug is being saved, so now the questions is why it does not sluggify automaticly on $vara->save();
Most probably, it's an issue of validation because you're using Ardent:
Ardent is a package that "provides self-validating smart models for Laravel Framework 4's Eloquent ORM"
Check your rules and use if statement:
if(! $vara->save()) // if model is invalid
dd($vara->errors());
If you don't need to check validation , you may use
$vara->forceSave();
To integrate Eloquent sluggable with Ardent, take a look at this link
Related
Is there a way to invoke eloquent relationship methods without changing the original eloquent collection that the method runs on? Currently I have to employ a temporary collection to run the method immutable and to prevent adding entire related record to the response return:
$result = Item::find($id);
$array = array_values($result->toArray());
$temp = Item::find($id);
$title = $temp->article->title;
dd($temp); //This prints entire article record added to the temp collection data.
array_push($array, $title);
return response()->json($array);
You are not dealing with collections here but with models. Item::find($id) will get you an object of class Item (or null if not found).
As far as I know, there is no way to load a relation without storing it in the relation accessor. But you can always unset the accessor again to delete the loaded relation (from memory).
For your example, this process yields:
$result = Item::find($id);
$title = $result->article->title;
unset($result->article);
return response()->json(array_merge($result->toArray(), [$title]));
The above works but is no very nice code. Instead, you could do one of the following three things:
Use attributesToArray() instead of toArray() (which merges attributes and relations):
$result = Item::find($id);
return response()->json(array_merge($result->attributesToArray(), [$result->article->title]));
Add your own getter method on the Item class that will return all the data you want. Then use it in the controller:
class Item
{
public function getMyData(): array
{
return array_merge($this->attributesToArray(), [$this->article->title]);
}
}
Controller:
$result = Item::find($id);
return response()->json($result->getMyData());
Create your own response resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ItemResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'title' => $this->article->title,
'author' => $this->article->author,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
Which can then be used like this:
return new ItemResource(Item::find($id));
The cleanest approach is option 3. Of course you could also use $this->attributesToArray() instead of enumerating the fields, but enumerating them will yield you security in future considering you might extend the model and do not want to expose the new fields.
I see two ways you can achieve that.
First, you can use an eloquent Resource. Basically it'll allow you to return exactly what you want from the model, so in your case, you'll be able to exclude the article. You can find the documentation here.
The second way is pretty new and is still undocumented (as fas i know), but it actually works well. You can use the unsetRelation method. So in your case, you just have to do:
$article = $result->article; // The article is loaded
$result->unsetRelation('article'); // It is unloaded and will not appear in the response
You can find the unsetRelation documentation here
There is not as far as I know. When dealing with Model outputs, I usually construct them manually like this:
$item = Item::find($id);
$result = $item->only('id', 'name', 'description', ...);
$result['title'] = $item->article->title;
return $result;
Should you need more power or a reusable solution, Resources are your best bet.
https://laravel.com/docs/5.6/eloquent-resources#concept-overview
I am having this error and none of the googled result i checked is similar to my problem.
I have an application with class Deal, User, and Matches
A deal has many matches.
A user has many matches.
A user has many deals.
I am attempting to create a new Match using my Deal object
$deal->matches()->create(['user_id'=>$id]);
This is my match class, i have defined all needed relationships
class Match extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $guarded = [];
public $timestamps = false;
public $expired_on = "";
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->matched_on = $model->freshTimestamp();
});
}
public function __construct(){
$d = (new \DateTime($this->matched_on))->modify('+1 day');
$this->expired_on = $d->format('Y-m-d H:i:s');
}
/**
* Get the user that owns the match.
*/
public function user()
{
return $this->belongsTo('App\User');
}
/**
* Get the deal that owns the match.
*/
public function deal()
{
return $this->belongsTo('App\Deal');
}
}
And i keep getting this error when i attempt to create a new match.
QueryException in Connection.php line 647:
SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into matches (deal_id) values (1))
I have my guarded to be an empty array, what could be the problem?
Remove the guarded array and add the fillable instead:
protected $fillable = ['user_id', 'deal_id'];
If you would like to revert to previous behavior, update your
config/database.php
file and set 'strict' => false for your connection.
Since it was a unique field in my case, I could not make it nullable.
For me, I had an empty constructor which was causing the issue don't know why.
Please comment if anyone knows the reason.
public function __construct(){
}
Commenting/removing it resolved the issue.
If you have a constructor in your model, just make sure it has a call to a parent constructor as well:
public function __construct( array $attributes = array() ) {
// mandatory
parent::__construct($attributes);
//..
}
Otherwise, it will break some functionality like Model::create.
Alexey Mezenin's Answer is correct and a good one.
Another way i used around it, for those who want to maintain the guarded empty array is to create a new Match object and put in the attributes and save.
$match->user_id = $id;
$match->deal_id = $deal->id;
$match->matched_on = $match->freshTimestamp();
$match->save();
I am using Laravel 8 and fixed this error thorugh this two steps:
move the word from $guarded array to $fillable array in User Mode
Config.database.php: 'strict' => false in the array of 'mysql'
Another way around this error is to include
'strict' => false,
into config/database.php within mysql array
When manually importing / exporting the databases, check if the transfer of all table settings was successful. If you forget to add an auto increment primary key, Laravel doesn't fill the value for you.
Adding the AUTO_INCREMENT afterwards will solve the problem.
I had this error but my wrong was making class model:
$book = new Book();
While this is true
$book = new Book($request->all());
changing your "config/database.php" won't help.
If you're getting this error, you're not sending the data to database correctly.
check your function in your controller, the create() method is probably being blocked by an if statement or something.
or
if it's an API, check the post request from the frontend that's where your issue is.
make sure the form is correctly passed into to request.
Currently the automatic scaffolding for search fields where there is an enum produces a drop down only allowing one selection to be made. I'm interested in using existing filters to change this to allow multiple selections.
Given the following dataobject...
class MyDataObject extends DataObject {
static $db = array(
'Name' => "Varchar(255)",
'MyEnum' => "Enum('Option1,Option2,Option3','Option1')"
);
}
...and the following ModelAdmin...
class MyModelAdmin extends ModelAdmin {
static $mangaged_models = array(
'MyDataObject',
);
static $url_segment = 'mymodeladmin';
static $menu_title = 'MyModelAdmin';
static $menu_priority = 9;
}
...I'm looking for a module or a simple Filter of some kind to scaffold the Enum into a multiple select listbox
the multiple select listbox is defined as...
Allows multiple selection
After typing some characters suggestions are offered
And I'm asking for a generic solution - I can build a search context for each model admin but this is very frustrating.
Something like the following using either an existing filter (ExactMatchMultiFilter looks perfect but doesn't seem to actually work) or if there is one in a module or someone can suggest how to modify an existing filter for this that would be great.
class MyDataObject extends DataObject {
static $db = array(
'Name' => "Varchar(255)",
'MyEnum' => "Enum('Option1,Option2,Option3','Option1')"
);
public static $searchable_fields = array (
'MyEnum' => array('filter' => 'ExactMatchMultiFilter')
);
}
Any help is much appreciated.
From your question, it seems like you were intending that the filter you pass to the searchable field would change the scaffolding. I've done a bit of digging and that doesn't seem to be the case. However, if you used the field option instead, you can likely achieve what you want.
You do specifically mention ListboxField and while it does support multiple, it isn't enabled by the default constructor on the field which is how it would be instantiated.
What you want could be accomplished out-of-the-box a bit more by the
CheckboxSetField. (I will admit, the UI is a bit average when used in ModelAdmin)
The resulting code could look something like this:
class MyDataObject extends DataObject {
static $db = array(
'Name' => "Varchar(255)",
'MyEnum' => "Enum('Option1,Option2,Option3','Option1')"
);
public static $searchable_fields = array (
'MyEnum' => array('field' => 'CheckboxSetField')
);
}
Unfortunately it isn't that easy, you will notice just by doing that it will come up saying "No options available" instead of a list of checkboxes. This is due to SilverStripe acting differently when we provide the field option that I mentioned earlier.
The workaround for such isn't great but is arguably still generic. I made an extension class of ModelAdmin, it looks for the CheckboxSetField in the search form and sets the Enum values for it.
class MyModelAdminExtension extends Extension {
public function updateSearchForm($form) {
$modelClass = $form->getController()->modelClass;
foreach ($form->Fields() as $field) {
if ($field->class == 'CheckboxSetField') {
//We need to remove the "q[]" around the field name set by ModelAdmin
$fieldName = substr($field->getName(), 2, -1);
$dbObj = singleton($modelClass)->dbObject($fieldName);
if ($dbObj->class == 'Enum') {
$enumValues = $dbObj->enumValues();
$field->setSource($enumValues);
}
}
}
}
}
That is a relatively safe ModelAdmin extension as it specifically looks for the combination of an Enum mapped to a CheckboxSetField which can only happen when you manually specify it.
Having gone this far, we actually could look back at the ListboxField, overcome the multiple option being disabled and populate it with values (as it would suffer the same problem mentioned above). This solution will be a little less generic as we will force all ListboxField's that were mapped from an Enum to be multiples but if we want a nicer solution, this is how we can get it.
class MyModelAdminExtension extends Extension {
public function updateSearchForm($form) {
$modelClass = $form->getController()->modelClass;
foreach ($form->Fields() as $field) {
if ($field->class == 'ListboxField') {
//We need to remove the "q[]" around the field name set by ModelAdmin
$fieldName = substr($field->getName(), 2, -1);
$dbObj = singleton($modelClass)->dbObject($fieldName);
if ($dbObj->class == 'Enum') {
$field->setMultiple(true);
$enumValues = $dbObj->enumValues();
$field->setSource($enumValues);
}
}
}
}
}
And for our model...
class MyDataObject extends DataObject {
private static $db = array(
'Name' => "Varchar(255)",
'MyEnum' => "Enum('Option1,Option2,Option3','Option1')"
);
public static $searchable_fields = array (
'MyEnum' => array('field' => 'ListboxField')
);
}
You now have what you wanted - a multi-select ListBoxField with Enum values.
You might be asking now, why did I cover CheckboxSetField? Well, I think it is important to look at all possible solutions. I came to the solution I provided through trying the CheckboxSetField and it really was only a last minute thing where I realised with some minor modifications, I could get it working for the ListboxField.
As you raised, there is an issue for the above code handling an Enum across a HasOne relationship. This is due to the ModelAdmin extension taking the field name and treating it exclusively like a database field (via dbObject) on the model class of the form. Instead, we can detect a relationship from the dual underscores on the field name, replacing it with dot-syntax and treating that instead like a relationship (via relObject).
Our updated updateSearchForm function would look like this:
public function updateSearchForm($form) {
$modelClass = $form->getController()->modelClass;
foreach ($form->Fields() as $field) {
if ($field->class == 'ListboxField') {
//We need to remove the "q[]" around the field name set by Model Admin
$fieldName = substr($field->getName(), 2, -1);
$dbObj = null;
//Check if the field name represents a value across a relationship
if (strpos($fieldName, '__') !== false) {
//To use "relObject", we need dot-syntax
$fieldName = str_replace('__', '.', $fieldName);
$dbObj = singleton($modelClass)->relObject($fieldName);
}
else {
$dbObj = singleton($modelClass)->dbObject($fieldName);
}
if ($dbObj != null && $dbObj->class == 'Enum') {
$field->setMultiple(true);
$enumValues = $dbObj->enumValues();
$field->setSource($enumValues);
}
}
}
}
Suppose I have a relationship between the following two Models in Laravel's Eloquent:
<?php
// user:
// - user_id
class User extends Model
{
protected $table = 'users';
public function settings()
{
return $this->hasMany('Setting');
}
public function settingSet($key, $value)
{
\Setting::setConfigItem($key, $value, $this->user_id);
}
}
// settting:
// - setting_key
// - setting_value
// - user_id
class Setting extends Model
{
public function setConfigItem($key, $value, $user_id)
{
// Note: I've provided this code here as an example, so it should
// exist here only as pseudo-code - it has not been tested and
// is outside the scope of this issue but has been requested by
// a commenter so I've provided the basis for this method:
$existing = \Setting::where(['key' => $key, 'user_id' => $user_id])->first();
if (!$existing) {
\Setting::insert([ 'setting_key' => $key, 'setting_value' => $value, 'user_id' => $user_id ]);
} else {
$existing->setting_value = $value;
$existing->save();
}
}
}
And I want to retrieve a single user and his settings, I can do the following:
<?php
$user = User::with(['setting'])->find(1);
Now, with this user, I can update or insert a setting using the settingSet method, as listed above.
<?php
$user->settingSet('foo','bar');
However, if I retrieve the settings at this point, I will get stale data.
<?php
print_r($user->settings); // onoes!
What's the best practice to force the data for this relationship to be updated after an INSERT/UPDATE/DELETE in the User::settingSet method or in other similar methods?
You can force the data to be updated by using Lazy Eager Loading, load() function.
print_r($user->load('settings'));
source: http://laravel.com/docs/5.0/eloquent#eager-loading
You have this issue due to using query builder instead of the eloquent,I dont understand why your using both,if you're using eloquent then use eloquent if you're using query builder use query builder,but dont use both,at least not when you have the possibility not to.
I find the setConfigItem method useless as you arent pushing a user into a setting but a setting into a user so basically all implementions should be on a user class and not on the settings class
After clearing that out,you could try doing something like this -
public function settingSet($key, $value)
{
$setting = new Setting([
'setting_key' => $key,
'setting_value' => $value
]);
$this->settings()->save($setting);
}
also you could improve this method by instead of accepting just 1 setting at a time you could accept array of settings
btw is there a reason why you arent using pivot table ? are the settings unique foreach user ?
I am new to cakephp. I have a problem with calling the function. here is my issue.
In Contrloller file i get all the values using the following function
public function index()
{
$conditions = array(
'order' => array('Histroy.chat_sk DESC')
);
$this->set('histroys', $this->Histroy->find('all',$conditions));
}
In My model file have the following,
class Histroy extends AppModel
{
public $tablePrefix = 'plc_';
public $useTable = 'chat_history';
}
In my view file i have listed the values using foreach() function and that as follows
foreach ($histroys as $histroy):
$oper_name = $histroy['Histroy']['operator_fk'];
$operator_email = $histroy['Histroy']['email'];
endforeach
in that opertaor_fk is a field in history table. So i need get the operator name by another table as operators. So i need to call that function in the view.
Ex : In core we can do like as,
$operator_name = operator_name($fetch['operator_id']);
Function should be like this:
function operator_name($id)
{
// Select the value for the matched field in the operator
return $operator_name;
}
In cakephp how can i retrieve the values.
Please help me out to fix this. Thanks in Advance
Follow the blog tutorial for cake. It'll explain how to create associations and relationships between tables to let you do what is is you want, but in a nutshell, you need to create a relationship between History and Operator models and work from there.