I'm working with Laravel 5.3 and in a Model Post, I have an appends attributes :
/*
* toJson
*/
protected $appends = ['count'];
And the magic method :
public function getCountAttribute()
{
return Offer::where('id', '=', $this->id)->count();
}
So, when I get a Post model with eloquent like Post::get(), and get the return with json for example, I ALWAYS have this attribute count in my object.
How can I specify if I want or not this or another appends atribute ?
I checked how Eloquent models get serialized and unfortunately list of fields to be appended is not shared between all instances of given Model and the only way I see to achieve what you need is to iterate through the result set and explicitly enable append for selected attributes:
$posts = Post::all();
foreach ($posts as $post) {
// if you want to add new fields to the fields that are already appended
$post->append('count');
// OR if you want to overwrite the default list of appended fields
$post->setAppends(['count']);
}
You could get the model's attributes with $post->getAttributes() which returns an array of attributes before any appends
Related
This question already has answers here:
How can i iterate over attributes in laravel models?
(2 answers)
Closed last year.
I have a problem with Laravel when I try to display my data via my view...
Here is my code to browse everything with my view :
#foreach($recipes as $t=>$d)
{{$t}} // doesn't display all the key
#endforeach
{{$recipes->LABEL}} // work great
Their is the result : incrementing preventsLazyLoading exists wasRecentlyCreated timestamps
Their is my controller and the model :
Model :
/**
* Get a recipe from its primary id : recipe_id
* If fail it return 404 exception
* Else return the collection with all the data of a recipe
*
* #param integer $id id of the recipe in the db
* #return Collection the collection that have been finded by his id
*/
public static function getRecipe($id)
{
return self::findOrFail($id);
}
Controller :
function getRecipe($id)
{
return view('recipe_detail')->with('recipes', Recipe::getRecipe($id));
}
Is their any solution to display with foreach and with call the key ?
Thanks
You have a single object $recipe that you are trying to loop on in your blade file.
This method:
public static function getRecipe($id)
{
return self::findOrFail($id);
}
returns the single object based on the $id. If you want a collection, skip the $id and use get() or all(), something like:
public function getRecipes(){
return self::all()
}
Or, just call it right in the controller without need for the method in the Model: $recipes = Recipe::all();
You can then loop on $recipes in the blade file because it is now a collection.
If you want to get all the "attributes" of the Model then you have to ask the Model for them; you can use the getAttributes method:
#foreach ($model->getAttributes() as $key => $value)
...
#endforeach
Kindly note this is not a duplicate of this or this
I have a model page with json field called options.
in my page model, i have added the attribute options to the $cast variable and in the $fillable variable.
Then i have a function pageOptions() that's suppost to return a class PageOptions like so:
/*
*#return pageOptions
*/
public function pageOptions() : PageOption
{
$options = ($this->options) ? ($this->options) : [];
return new PageOption($options, $this);
}
the Page option class simply exposes functions of set() and get() to enable me set and get json data from the options field.
the set() method, simply sets data to a $options variable then calls the persist method that pushes data to the options field in the database.
/**
* Persist the options.
*
* #return mixed
*/
protected function persist()
{
return $this->model->update(['options' => $this->options]);
}
Instead
i get this error
Object of class stdClass could not be converted to string
i am not trying to echo any object or array anywhere in my code so i cannot really understand where the error is coming from . . I have tried to json_encode the options variable manually in the persist() method but i still get the same error. I also tried adding the TOString() magic method on the pages and PageOptions classes like so
public function __toString()
{
return $this->name;
}
but still does not solve it.
You should use the $casts property on the model.
protected $casts = [
'options' => 'array'
];
From the docs:
The array cast type is particularly useful when working with columns
that are stored as serialized JSON. For example, if your database has a JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model.
I've a very big doubt about how works laravel for a very simple thing:
If I call:
$companies=User::All();
Then I can use statement like this in a forach:
foreach($companies as $company)
$company['new_field']= 'something';
If i'm limiting the output of the query like:
$companies = DB::table('companies')
->select('id','name','email','business_name',...)->get();
The things doesnt work as before,
I try with or without the ->get()
I try to convert with ->toArray() (errors rised)
I try with put() and push() for collections method and agains errors...
How can I add a field in every item of the collection just to pass it to a view?
Try like this, hope it works for you:
$users=User::select('id','name','email','business_name',...)->get()->toArray();
and then use foreach loop like this:
foreach($users as $key => $value ){
$users[$key]['newField'] = "Demo";
}
If you are using Laravel and model in it so there is a better way to add custom attribute or field here is what i do for custom field
For Example :
There is a Model Name User
so in User Model
add a property name appends like :
class User extends Model
{
protected $appends = ['new_field'];
public function getNewFieldAttribute() // defining field logic here
{
return // your code
}
So you no need to use foreach and looping and adding new field
for more have a look on laravel doc : https://laravel.com/docs/5.5/eloquent-mutators#accessors-and-mutators
Suggestion
you can limit your output with Model too.
User::select('id','name','email','business_name',...)->get();
if you are making an array like
User::select('id','name','email','business_name',...)->get()->toArray();
so this will also give you your custom field
I have a situation where I need a specific attribute accessor appended to one of my models automatically:
class Mission extends Eloquent {
protected $appends = ['launch_date_time'];
public function getLaunchDateTimeAttribute() {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
}
As you can see, this launch_date_time property is dependent on two other fields of my model that are actually in my database.
However, I now want to perform a query where only a certain number of fields are returned, as this is going to be sent over AJAX multiple times and I would rather use as few resources as possible:
// AJAX GET
// missions/all
public function all() {
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image']);
return Response::json($allMissions);
}
The issue here is that I no longer need the launch_date_time attribute, so I have excluded it, **in doing so, my AJAX request does not work successfully:
Undefined index: launch_approximate on line 78 of H:\myproj\app\models\Mission.php
This is clearly because my model is attempting to append launch_date_time, of which launch_approximate is a dependency of. If I include all the required dependencies, all of them any my attribute that I want to append appear:
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image', 'launch_approximate', 'launch_exact', 'launch_date_time']);
This is undesirable. Is there a solution where I can keep both setups?
The reason it is not working is because you are not retrieving the required fields from the database in the get method on your query. That is why you can't access launch_exact and launch_approximate because they are not set in the instance of your model.
So to make it work like you want. You would have to check if launch_exact and launch_approximate are set before you access them.
public function getLaunchDateTimeAttribute() {
if(isset($this->attributes['launch_approximate']) && $this->attributes['launch_exact']) {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
return null;
}
You can also set a whitelist with the $visible property and a black list with $hidden inside your model to not show certain attributes when outputing to json or a array take a look at the docs: http://laravel.com/docs/5.1/eloquent-serialization#hiding-attributes-from-json
I am raising a Yii event on beforeSave of the model, which should only be fired if a specific property of the model is changed.
The only way I can think of how to do this at the moment is by creating a new AR object and querying the DB for the old model using the current PK, but this is not very well optimized.
Here's what I have right now (note that my table doesn't have a PK, that's why I query by all attributes, besides the one I am comparing against - hence the unset function):
public function beforeSave()
{
if(!$this->isNewRecord){ // only when a record is modified
$newAttributes = $this->attributes;
unset($newAttributes['level']);
$oldModel = self::model()->findByAttributes($newAttributes);
if($oldModel->level != $this->level)
// Raising event here
}
return parent::beforeSave();
}
Is there a better approach? Maybe storing the old properties in a new local property in afterFind()?
You need to store the old attributes in a local property in the AR class so that you can compare the current attributes to those old ones at any time.
Step 1. Add a new property to the AR class:
// Stores old attributes on afterFind() so we can compare
// against them before/after save
protected $oldAttributes;
Step 2. Override Yii's afterFind() and store the original attributes immediately after they are retrieved.
public function afterFind(){
$this->oldAttributes = $this->attributes;
return parent::afterFind();
}
Step 3. Compare the old and new attributes in beforeSave/afterSave or anywhere else you like inside the AR class. In the example below we are checking if the property called 'level' is changed.
public function beforeSave()
{
if(isset($this->oldAttributes['level']) && $this->level != $this->oldAttributes['level']){
// The attribute is changed. Do something here...
}
return parent::beforeSave();
}
Just in one line
$changedArray = array_diff_assoc($this->attributes,
$this->oldAttributes);
foreach($changedArray as $key => $value){
//What ever you want
//For attribute use $key
//For value use $value
}
In your case you want to use if($key=='level') inside of foreach
Yii 1.1: mod-active-record at yiiframework.com
or Yii Active Record instance with "ifModified then ..." logic and dependencies clearing at gist.github.com
You can store old properties with hidden fields inside update form instead of loading model again.