Eloquent $appends attributes returning null - php

I am loading some data into an Eloquent model via an appended attribute and the returned model's attribute is always null. I have protected $appends = array('survey_status); defined in my model (named Survey), and have the accessor defined as such:
public function getSurveyStatusAttribute($value){
return $value;
}
I have tried setting the attribute both as a property and in bracket notation($this->survey_status = ... & $this->attributes['survey_status'] = ..) and also tried using the setAppends() method prior to returning the model, all to no avail.
This was reported on the Laravel forums back at the end of Sept. 2013 and reported as fixed that Oct. (see: https://github.com/laravel/framework/issues/2336 and http://laravel.io/forum/02-26-2014-model-attribute-accessor-not-working-with-object-get-helper-function)
I am on the most current version of Laravel 4 (v4.2.17) which was released in Feb of this year. And from what I read in the docs and elsewhere it seems as though I'm doing everything correctly. Can any see something I'm not doing or confirm this is still an issue?
UPDATE
So I think I figured out 75% of my issue. I didn't realize you could pass an array to $model->load() to make complex queries using where/orWhere/etc. So this basic example works:
$survey = Survey::find(168);
$survey->load(array('surveyStatus' => function ($q){
$q->where('organization_id', '=', 7485);
}));
return Response::json($survey);
In the response my SurveyStatus model is supplied. My issue now is I am trying to iterate of a collection of Survey models to add a SurveyStatus relation just like the working one above but the attribute isn't there on the response. This is what I'm using to iterate the collection:
$org->surveySets->each(function ($ss) use ($id){
$fye = $ss->fiscal_year_end;
$ss->surveys->each(function ($survey) use ($id, $fye){
$sid = $survey->id;
$survey->load(array('surveyStatus' => function ($q) use($id, $fye){
$q->where('organization_id', '=', $id)->where('fiscal_year_end', '=', $fye);
}));
$survey->content_groups->each(function ($cg) use ($id, $sid, $fye){
$cg->content_rows->each(function ($cr) use ($id, $sid, $fye){
$cr->questions->each(function ($q) use ($id, $sid, $fye){
// do the same thing as surveyStatus but load surveyData relation into question
});
});
});
});
});
Is there some reason the loading doesn't 'stick' when iterating over a collection?

Correct me if I'm wrong, but appends doesn't get passed a $value because it's not mapped to a table column. I always thought of it as a computed property of sorts.
Given we have fillable columns 'first' and 'last' we might create an appends called 'fullname'.
protected $appends = [
'fullname'
];
public function getFullnameAttribute()
{
return $this->attributes['fullname'] = $this->first . ' ' . $this->last;
}
Essentially what I think your confusing is that appends is extra data that your appending to your model attributes. You are expected to return a value from the accessor. Returning $value will be null because $value doesn't exist, which is why your manually appending it. Try returning 'foo' in your accessor then you'll see what I mean.

Hello if you want to append some extra data related to another model you could do something like this.
protected $appends = [
'catName'
];
// relation
public function category()
{
return $this->hasOne(PostCat::class, 'id', 'id_cat');
}
//getters
public function getCatNameAttribute()
{
return $this->category()->getEager()->first()->name;
}
If your related model hold many db row considere this
protected $with = [
'category'
];
protected $appends = [
'catName'
];
// relation
public function category()
{
return $this->hasOne(PostCat::class, 'id', 'id_cat');
}
//getters
public function getCatNameAttribute()
{
return $this->category->name;
}
best regards

Related

Laravel ->with(") is there a way to use it with attributes?

I've been using ->("with) for relationships when querying models like so...
return Model::
where('set_id', $request->set)
->without('set')
->with('user:id,first_name,last_name,profile_photo')
->get();
Which works great for a relationship:
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
But what about doing the same with an attribute?
public function getPermissionsAttribute(){
$permissions = $this->permissions;
$new = [];
foreach($permissions as $p){
$new[$p->permission] = true;
}
return json_encode($new);
}
Is there a way to do this in laravel? As at the moment I'm getting the following error...
App\Models\User::permissions must return a relationship instance. (View: C:\Users\Nick\PhpstormProjects\laravel-vue\resources\views\index.blade.php)
I did have it in:
public $appends = [
'full_name',
'profile_photo_thumb',
'permissions'
......... etc ............
];
But, the list was getting longer and I was sending out data to the front end that largely wasn't used, so was causing longer loading
You can append accessor at Run Time to help you decide what data you want serialized in the response by calling append on your Eloquent Collection:
return $collection->append('attribute');
Or for multiple, you can pass an array:
return $collection->append(['attribute1', 'attribute2']);
Laravel 8.x Docs - Eloquent - Serialization - Append Values to JSON - Appending at Run Time append
Try to get it by select function like this
return Model::where('set_id', $request->set)->with(['user' => function ($query) {
$query->select('id','first_name','last_name','profile_photo');
}])

Error when trying to use eloquent relationships

I am trying to get this problem fixed for hours now and I just don't get what I am doing wrong.
I want to get all entries of my tables where my id of posts equals the id of tables bewerbungens.
So I get all applications of every user existing with the corresponding title of the post the user applied to.
I asked a question here before and got resources to add relationships, which I did, as seen here:
Bewerbungen Model:
protected $fillable = [
'bewerber_email',
'Stellenanzeigen_ID',
'is_Accepted',
'accept_Date',
'is_Canceled',
'cancel_Date',
'is_Received',
'receive_Date',
'is_Canceled_Bewerber',
'cancel_Date_Bewerber',
'lebenslauf_File',
'zeugnis_File',
'anschreiben_File',
'weitere_Doks',
];
public function post() {
return $this->belongsTo('Post', 'Stellenanzeigen_ID', 'Bewerbung_ID');
}
and here in my Post model:
protected $fillable = [
'titel',
'startdate',
'enddate',
'beschreibung',
'standort',
'type_name',
'abteilung_name',
'kontakt',
'isActive',
'lebenslauf',
'zeugnisse',
'anschreiben',
'weitere_Doks',
'is_Permitted',
'job_start',
];
public function bewerbungen() {
return $this->belongsToMany(Bewerbungen::class);
}
Here is my Controller code where I try to get the entries:
$dummy = Post::with('Bewerbungen.post')->get();
But I get the error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'portal.bewerbungen_post' doesn't exist (SQL: select bewerbungens.*, bewerbungen_post.post_id as pivot_post_id, bewerbungen_post.bewerbungen_id as pivot_bewerbungen_id from bewerbungens inner join bewerbungen_post on bewerbungens.id = bewerbungen_post.bewerbungen_id where bewerbungen_post.post_id in (1, 2, 3, 4))
I also tried this instead:
$dummy = Post::where('id', 'Stellenanzeigen_ID')->get();
But it just returns an empty set.
How can I fix this?
Edit: Post Model
protected $fillable = [
'titel',
'startdate',
'enddate',
'beschreibung',
'standort',
'type_name',
'abteilung_name',
'kontakt',
'isActive',
'lebenslauf',
'zeugnisse',
'anschreiben',
'weitere_Doks',
'is_Permitted',
'job_start',
];
public function location() {
return $this->hasOne(Standort::class);
}
public function job_type() {
return $this->hasOne(Jobtypes::class);
}
public function bewerbungen(): BelongsToMany
{
return $this->belongsToMany(Bewerbungen::class, 'bewerbungens');
}
I had the bewerbungen() method as suggested in the comment, I just played around with it so it looks different, but it is currently not in use since it didn't work
The SQL Error said that there's no bewerbungen_post table, since you use belongsToMany with only one argument. As mentioned here, if your relationship's intermediate table name is different with the Laravel Behavior, then add the relationship's intermediate table name to the second argument, or Laravel will join the two table for the relation table's name. For example:
public function bewerbungen() {
return $this->belongsToMany(Bewerbungen::class, 'bewerbungen_post');
}
I've checked your previous question, and I think that the Post model joined directly to the Bewerbungen model, so the relation in the Post Model should looks like this:
public function bewerbungens(): HasMany
{
// It's mean that `Post` has many `Bewerbungen` where `Stellenanzeigen_ID` = `posts.id`
return $this->hasMany(Bewerbungen::class, 'Stellenanzeigen_ID', 'id');
}
And in the Bewerbungen model:
public function post(): BelongsTo
{
// It's mean that `Bewerbungen` owned by Post as `Stellenanzeigen_ID` = `posts.id`
return $this->belongsTo(Post::class, 'Stellenanzeigen_ID', 'id');
}
If you want to get Post with all Bewerbungen owned by Posts:
// You will get All Post with all Bewerbungen each Post
$posts = Post::with('bewerbungens')->get();
Vice versa:
// You will All Bewerbungen and One Post for every Bewerbungen
$bewerbungen = Bewerbungen::with('post')->get();
All details about relationship could be found in Laravel Documentation.

Laravel model - uncast specific column

I have a laravel table with a column I've defined like this in the migration:
$table->json('json');
And in the model, I cast it to an array:
protected $casts = [
'json' => 'array'
];
This works perfectly the majority of the time I need it, but there's one api call I'm making where I actually want my collection of that Model to give me the raw string rather than casting it to the array.
So, assuming my model is called Model, my api call looks like this:
$systemModels = Model::whereNull('user_id')->get();
$userModels = Model::where('user_id', $user->id)->get();
return response()->json([
'user_models' => $userModels->toArray(),
'system_models' => $systemModels->toArray()
]);
This is where I'd like the 'json' column of my Model to be rendered as a string rather than cast to an array. Is there a reliable way to do that?
Inside your model you can define a custom attribute which is added when the model is serialized:
class YourModel extends Model
{
protected $appends = ['json_raw'];
public function getJsonRawAttribute()
{
return $this->attributes['json'];
// or return json_encode($this->attributes['json']);
}
}
And then when doing the toArray() you can do $userModels->makeHidden('json')->toArray(); to remove the casted field you do not want.

use relationship in model accessor in laravel

Suppose I have a Course model like this :
class Course extends Model
{
public $primaryKey = 'course_id';
protected $appends = ['teacher_name'];
public function getTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
}
public function teacher ()
{
return $this->belongsTo('App\User', 'teacher', 'user_id');
}
}
And in the other hand there is a User model like this :
class User extends Authenticatable
{
public $primaryKey = 'user_id';
protected $appends = ['full_name'];
public function getFullNameAttribute ()
{
return $this->name . ' ' . $this->family;
}
public function course ()
{
return $this->hasMany('App\Course', 'teacher', 'user_id');
}
}
As you can see there is a hasMany relationship between those.
There is an full_name accessor in User model.
Now I want to add a teacher_name accessor to Course model that uses it's teacher relations and gets full_name of teacher and appends to Course always.
In fact I want whenever call a Course model, it's related teacher name included like other properties.
But every time , when call a Course model , I got this error :
exception 'ErrorException' with message 'Trying to get property of non-object' in D:\wamp\www\lms-api\app\Course.php:166
That refers to this line of Course model :
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
I do not know how can I solve that and what is problem exactly.
Yikes some interesting answers here.
FYI to those coming after me- getFooAttribute() should return the data, and not modify the internal attributes array.
If you set a new value in the attributes array (that doesnt exist in this model's db schema) and then attempt to save the model, you'll hit a query exception.
It's worth reading up the laravel docs on attribute accessors/mutators for more info.
Furthermore, if you need to access a related object from within the model (like in an accessor) you ought to call $related = $this->getRelation('foo'); - note that if the relation isnt loaded (e.g., if you didnt fetch this object/collection with eager loaded relations) then $this->getRelation() could return null, but crucially if it is loaded, it won't run the same query(ies) to fetch the data again. So couple that with if (!$this->relationLoaded('foo')) { $this->loadRelation('foo'); }. You can then interact with the related object/collection as normal.
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
Should be
$this->attributes['teacher_name'] = $this->teacher->full_name;
First thing is that you want to reference the relationship, so loose the brackets (), and because the relationship is belongsTo, you will have one user / teacher returned. So you don't need the first().
We haven't seen your fields but probably you will have to change:
return $this->belongsTo('App\User', 'teacher', 'user_id');
to
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
where foreign_key and other_key are the primary keys that you need to make the join on.
Check this link from the documentation for reference:
https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse
the right way to do this is:
COURSE
public function setTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher->full_name;
}
100% working for me.
I have one to one relationship between Order and Shipment. I have to add the accessor of shipments table column from orders table.
function getOrderNoAttribute()
{
$appendText = "OR100";
if($this->orderShipment()->first()) {
$appendText = $this->orderShipment()->first()->is_shipping === 1 ? "ORE100" : "OR100";
}
return $appendText . $this->attributes['id'];
}
This error is only object data to array use or array data to object data use.
example::
$var->feild insted of $var[feild]
$var[feild] insted of $var->feild
You should use return for accessors . something like this :
public function getTeacherNameAttribute ()
{
return $this->teacher()->first()->full_name ?? '';
}
maybe a course hasn't teacher.

Laravel Eloquent Save to DB Using Create - Unhelpful Error

I get a very unhelpful error when I try and insert a new record in to my db. Does anyone have any idea where to start to solve this error?
user_id
That's all it says. (This is the name of one of my required fields in the table I'm saving to but it's not very descriptive.)
For reference here's my code:
$data = array(
'user_id'=>1,
'post_type'=>'0',
'post_title'=>'blah',
'description'=>'blah');
//it fails on this line
$id = Post::create($data);
Here's what my model looks like:
class Post extends Eloquent{
public static $rules = array(
'user_id'=>'required',
'post_type'=>'required',
'post_title' => 'required|max:25',
'description'=>'required'
);
public static function validate($data){
return Validator::make($data, static::$rules);
}
public function user(){
return $this->hasOne('User', 'id', 'user_id');
}
}
Tried getting rid of all relationships and validation in the model. That didn't work.
This is called mass-assignment in Laravel, what you need to do to get it working on your model is to set a protected array called $guarded to be empty. Add this to your model.
protected $guarded = array();
What this array does, it can prevent attributes to be mass-assigned with an array, if you don't want an attribute to be filled with the Model::create() method, then you need to specify that attribute in the $guarded array.
If instead you want to specify only the fillable attributes, Laravel's Eloquent also provides an array called $fillable where you specify only the attributes that you want to fill via the mass-assigment way.
Further reading:
Mass Assignment:
http://laravel.com/docs/eloquent#mass-assignment
Create Method:
http://laravel.com/docs/eloquent#insert-update-delete

Categories