How to assign value to my custom property ? I am comming from Yii2 background and there is pretty straight forward. Here I made this:
protected $appends = ['sites'];
public function getSitesAttribute()
{
return $this->sites;
}
public function setSitesAttribute($value)
{
$this->attributes['sites'] = $value;
}
Trying to assign like mymodel->sites = ['1', '2', '3'] but without success. After trying to get the mymodel->sites value error is thrown: undefined property 'sites'.
Your accessor is not quite correct, note that you're not returning $this->attributes['sites']; but instead $this->sites.
class MyModel extends Model
{
public function getSitesAttribute()
{
return $this->attributes['sites'];
}
public function setSitesAttribute($value)
{
$this->attributes['sites'] = $value;
}
}
Then;
$my_model->sites = [1,2,3];
$my_model->sites; // [1,2,3]
You might want to set an initial default value for $this->attributes['sites'] in the model constructor.
The problem lies with the $this->sites. It is correct that it will throw an exception that you class instance does not have a property $sites.
Take a look at the __get method of Model. You can see here that a getAttribute($key) call is being made. Diving even deeper you would find out that at some point the following code is being executed in the transformModelValue method:
if ($this->hasGetMutator($key)) {
return $this->mutateAttribute($key, $value);
}
This piece of code calls your getSitesAttribute method.
Going back a little bit and you will see that it tries to retrieve the value for the $key (sites) from the attributes array by calling the getAttributeFromArray($key). This will return the value stored in the attributes array which is where your setSitesAttribute mutator stores the value.
The following options I could come up with:
Remove the accessor to let the model retrieve the value from the $attributes array.
protected $appends = ['sites'];
public function setSitesAttribute($value)
{
$this->attributes['sites'] = $value;
}
Store value in and retrieve from $sites property. Not sure if $appends still works.
protected $appends = ['sites'];
private $sites = [];
public function getSitesAttribute()
{
return $this->sites;
}
public function setSitesAttribute($value)
{
$this->sites = $value;
}
Related
There's classes and schedules. Strictly, one SchoolClass to one Schedule.
The creation of a Schedule depends on a SchoolClass, so I decided to inject SchoolClass on constructor to constrain the association.
class Schedule extends Model
{
// ...
private $class;
public function __construct(SchoolClass $class)
{
$this->class = $class;
}
// Used to limit domain, based on the associated class_id
protected static function booted()
{
static::addGlobalScope('class', function (Builder $builder) {
$builder->where('class_id', $this->class->id);
});
}
public function getClassIdAttribute()
{
return $this->class->id;
}
// ...
}
By this way, I can obtain the class_id associated with the Schedule
new Schedule(SchoolClass::find(1))->class_id; // Returns 1, as supposed
When creating a Schedule, associated to a SchoolClass, I want that builder consider the class_id attribute, but when I try to save it:
new Schedule(SchoolClass::find(1))->save(); // General error: 1364 Field 'class_id' doesn't have a default value
You've probably not set class_id in the fillable array. If you do that or make an empty guarded array, the error should be resolved.
I found a possible solution.
Updating model $attributes on the constructor:
public function __construct(SchoolClass $class)
{
$this->class = $class;
$this->attributes['class_id'] = $class->id;
}
So, until now the model wasn't fullfiled with class_id, and wasn't considering that field on save() method.
By the way, I believe there will be a better solution.
let's say that I want to get the author's name inside the book model
// app/Book.php
protected $appends = ['author_name'];
public function author() {
return belongsTo(Author::class, 'author_id');
}
public function getAuthorNameAttribute() {
return $this->author->name;
}
but this would append the whole author collection to the final book collection as well, that would bump up the loading time when trying to load like 100 books, right now I work it around by removing the author after getting the name like this
// app/Book.php
protected $appends = ['author_name'];
public function author() {
return belongsTo(Author::class, 'author_id');
}
public function getAuthorNameAttribute() {
$authorName = $this->author->name;
unset($this->author);
return $authorName;
}
is there a better way to do it? or did I miss any function from eloquent?
Cheers
Try to add following code in your app/Book.php
// app/Book.php
protected $hidden = ['author'];
I've used setNotification method of model by initial data from Controller within a variable $data as array. I have used self:: in this method instead of used table or Notification::save() or $obj->save(). by this way I don't know how to get Id which the last id after insert was done in laravel because I used $this->attributes that it is the protected variable in Model.
class Notification extends Model
{
protected $table = 'notification';
public $timestamps = true;
private $_data = false;
public function setNotification($data)
{
if (is_array($data)) {
$this->attributes = $data;
self::save();
}
}
}
Try something like $this->attributes[id] after save() is executed.
I suggest You to use create method instead and return created object, so then You can access id property.
public function setNotification($data)
{
if (is_array($data)) {
return $this->create($data);
}
return null;
}
I want to convert created_at dates to Persian date. So I implemented getCreatedAtAttribute function to do that. Because I just want to convert dates in special situations, I declared $convert_dates property in the model with default value as false.
class Posts extends Model {
public $convert_dates = false;
/**
* Always capitalize the first name when we retrieve it
*/
public function getCreatedAtAttribute($value) {
return $this->convert_dates? convert_date($value): $value;
}
}
$Model = new Posts;
$Model->convert_dates = true;
$post = $Model->first();
echo $post->created_at; // Isn't converted because $convert_dates is false
As you see in the codes above, it seems the model properties will be re-initial in mutators so the value of $convert_dates is always false.
Is there any other trick or solution to solve this problem?
This way you can set the constructor.
public function __construct($value = null, array $attributes = array())
{
$this->convert_dates = $value;
parent::__construct($attributes);
}
Now you can access this value in your mutator.
public function getCreatedAtAttribute($value)
{
return $this->convert_dates ? convert_date($value) : $value;
}
OR
Fill the protected fillable array like this:
class DataModel extends Eloquent
{
protected $fillable = array('convert_dates');
}
Then initialize the Model as:
$dataModel = new DataModel(array(
'convert_dates' => true
));
I am using Laravel 4.2.
I have two models: User and Video, both of these models are having one-to-many relationship i.e. User -> HasMany-> Video.
Recently, I got a requirement to display the list of users along with sum of file-size of total videos uploaded by each user and allow users to be order by the sum of file size ascending or descending.
I've made following changes in User model:
class User extends Eloquent {
protected $hidden = array('videosSum');
protected $appends = array('videos_size');
public function videosSum() {
return $this->hasOne('Video')
->selectRaw('sum(file_size) as sum, user_id')
->groupBy('user_id');
}
public function getVideosSizeAttribute()
{
// if relation is not loaded already, let's do it first
if ( ! array_key_exists('videos_size', $this->relations)){
$this->load('videosSum');
}
$related = $this->getRelation('videosSum');
return $this->attributes['videos_size'] = isset($related->sum) ? (int) $related->sum : 0;
}
}
And using like:
User::where('id', '!=', Auth::user()->id);
I am getting the desired result.
But the problem is, I don't want the videos_size attribute everywhere, where the User model gets called. I want to set it dynamically.
I tried User::$appends = ['videos_size'] but it gives protected property cannot be set outsize of class error.
I also tried to make a method in User model which set the $appends if called, but it is also not working.
Can anybody help me how to enable the appends property dynamically?
Laravel doesn't support this off the bat.
my friend and I wrote this extention:
Dynamically hide certain columns when returning an Eloquent object as JSON?
basically you have to override your models.php toArray() method as appended attributes get calculated when you ask for the model in json or array form.
you can add to the trait that's in that link and use it or just put these methods in your respective model class.
public static function getStaticAppends() {
return self::$_appends;
}
public static function setStaticAppends(array $value) {
self::$_appends = $value;
return self::$_appends;
}
public static function getDefaultAppends() {
return with(new static)->getAppends();
}
public function getAppends(){
return $this->appends;
}
public function toArray() {
if (self::getStaticAppends()) {
$this->appends = self::getStaticAppends();
}
return parent::toArray();
}