Laravel HasMany create child record - laravel

I'm having a problem when I'm trying to create a child model associated to his parent.
I have 2 models:
Instituion (has many addresses)
Address (has 1 Institution)
When I click a Institution and I go to show.blade.php where I have a form to create an Address for that Institution I can't get from the request the field "instituion_id" which relates child with parent.
This is shown in the request:
Request Data
From the form I'm calling the following route: action="/institution/{{ $institution->id }}/addresses"
This is the code in AddressController:
public function store(Request $request)
{
// dd($request->all());
Address::create($request->all());
return back();
}
When I test it, the DB is asking for the isntitution_id column:
SQLSTATE[HY000]: General error: 1364 Field 'institution_id' doesn't
have a default value (SQL: insert into addresses (address_name,
address_number, address_full_name, address_city,
address_state, address_postal_code, address_country,
updated_at, created_at) values (a, a, a, a, a, a, a, 2017-12-14
14:06:47, 2017-12-14 14:06:47))
So, how to I pass from the form the field "institution_id" and how do I get it in the controller and assocuiated to the child record?
I read many posts but in all of them they are just create a child record with one field but I want to process the complere request with all Addres fields.
Regards

You can access the id from the parameters of the store method like this :
In the routes file :
Route::post('/institution/{institution_id}/addresses', 'YourController#store')
In the controller :
public function store($institution_id, Request $request)
{
$request->request->add(['institution_id' => $institution_id]);
// dd($request->all());
Address::create($request->all());
return back();
}
Ps : Don't forget to add institution_id to the fillable fields in the Address Model.

If all relationships are set, the documentation has what you're looking for
https://laravel.com/docs/5.5/eloquent-relationships#the-create-method
Example:
// Routes (If laravel version > 5.2 web.php I guess
Route::post('/institution/{institution_id}/addresses', 'Controller#store')
// app/Http/Controllers/Controller.php
public function store(Request $request)
{
$institution = Institution::find($request->institution_id);
$address = $institution->addresses()->create($request->all());
return back();
}
// app/Institution.php
class Institution extends Model
{
public function addresses()
{
return $this->hasMany('App\Address', 'institution_id', 'id');
}
}

Here's how I did it. I had 2 models/controllers: website and visitor. Visitor is dependent on website.
Routes:
Route::resource('website', 'WebsiteController');
Route::resource('website/{website}/visitor', 'VisitorController');
Don't forget to set all the relationships. I'll only show you the one that I'll use to save the data, in the Website model:
public function visitors()
{
return $this->hasMany('App\Visitor');
}
Then, in the store inside the VisitorController:
public function store(Request $request, Website $website)
{
return response()->json(
$website->visitors()->create([
... fields without website_id (the relationship field) ...
]),
201
);
}

$p=Parent::find($anId);
$ch=Child::create(['somekey0'=>value0,'somekey1'=>value1,
...,'somekeyN'=>valueN]);
$ch->my_parent()->associate($p);
$ch->save();

Related

Route model binding select specific columns with relation

Route:
Route::get('/posts/{post}', [PostController::class, 'show']);
Controller:
public function show(Post $post){
$postData = $post->load(['author' => function($query){
$query->select('post_id', 'name');
}])
->get(['title', 'desc', 'created_date'])
->toArray();
}
This returns all the posts in the database, While I only want to get the selected post passed to the show function.
So if I visit /posts/3, It should show data related to the post with id = 3, not all posts.
The result should be an array with only the selected post.
When you are inside the Controller you already have the Post you want. If you call get you are getting all posts with a new query.
To select specific columns from a relationship, you can do it like this:
public function show(Post $post)
{
$post->load('author:id,name,age'); // load columns id, name, age from author
}
Notice that including the id is required.
To specify columns for the Post, there's no way to do it per route, the only way is to override how Laravel resolves the implicit binding. For that, you can add this in your Post model class:
public function resolveRouteBinding($value, $field = null)
{
return $this->whereKey($value)->select(['id', 'body', 'author_id'])->firstOrFail();
}
Notice that including the author_id is required to load the Author afterwards inside the Controller method. Also keep in mind this will affect all routes where you implicitly load a Post.
Please don't use get() function on the model.
public function show(Post $post)
{
$post->load(['author' => function ($query) {
$query->select(['post_id', 'name']);
]);
dd($post->toArray());
}
Btw, the author shouldn't have a post_id, because author can have multiple posts. you should update the database structure or you are doing wrong in the controller. (my bad, Thanks #techno)

Eloquent relationship: return just one value not entire row

In my laravel project I built a comment section and want to display the names of the people commenting beside their comment.
Initially I have just the userID, so I built a relationship (hasOne) linking the comment table (comment & authorID) to the users table (id (authorID) & username)
Comment.php (model) is:
[..]
public function author()
{
return $this->hasOne(User::class, 'id', 'Author');
}
The User.php model is:
<?php
[..]
public function author()
{
return $this->hasMany(Comment::class, 'Author', 'id');
}
In the controller I get the data with:
$comments= Comments::where('LinkID', (string) $id)->with('author')->orderBy('updated_at', 'ASC')->get()->all();
This works but it gives me the entire row of the user per comment. For security reasons I just want to return the 'name' field of the row (username) without the rest (email, timestamps etc.).
How can I achieve this?
please try:
$comments= Comments::where('LinkID', (string) $id)->with(['author' => function ($q) {
$q = $q->select('name', 'id');
return $q;
}
])->orderBy('updated_at', 'ASC')->get()->all();
or another way:
$comments= Comments::where('LinkID', (string) $id)->with('author:id,name')->orderBy('updated_at', 'ASC')->get()->all();
see eager loading (section Eager Loading Specific Columns)
https://laravel.com/docs/7.x/eloquent-relationships#eager-loading
note that including 'id' is necessary because it 's responsible for the relation

How can add additional Attribute in my JSON response with dynamic value in laravel

In my app i have a Posts and a Reacts table both are connected with relationship.
In App user can react to a post(like or dislike) and for retrieve this i'm using this function :
public function feed()
{
$posts=Post::with('user')
->with('reacts')
->withCount('comments')
->orderBy('created_at', 'DESC')
->get();
return response()->json(["posts" => $posts]);
}
the response is:
i want to add one more field in Posts Object for isUserLiked and if the current authenticated user liked the post then value will be true or false for him something like this:
i can add a additional field but how can i set the value dynamically for that
this is what i am doing in my Post Model:
protected $appends = ['isUserLiked'];
public function getIsUserLikedAttribute($id)
{
$react=React::where('user_id',auth()->user()->id)->where('post_id',$id)->exists();
return $react;
}
this is returning false because i don't know any way to pass the arguments(Post id).
is there any better way i can get the desired response? Thanks!
public function getIsUserLikedAttribute($id)
{
return React::where('user_id',auth()->user()->id)->where('post_id',$this->id)->exists();
}
In your user model:
public function reacts(){
return $this->hasMany(React::class);
}
public function scopeReactOnPost($query, $post_id){
return $this->reacts()->where(function($query) use ($post_id){
$query->where('post_id',$post_id);
});
}
and in your controller:
$user->reactOnPost($post_id)->first();
or
$user->reactOnPost($post_id)->get()->count();
Will let you know if user had any reaction on the specified post.
and for adding this to your json output you can artisan make a resource for your post model. Laravel Resources

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Loop through relationships Laravel

Stackoverflow,
What I want to accomplish:
I want to Loop through the database to find all the (Heroes) with their related (Interview). I want to also pull each Interview with it's related (Story) and (Image). So far I can dd($heroes) and I can see the array correctly grab each hero with their interview and each interview has it's associated image and story. How do I loop through this correctly?
Error
Invalid argument supplied for foreach() (View: /Users/plastics1509moore/Desktop/elephant_gin/resources/views/administration/index.blade.php)
This is what I have done:
Controller:
$heroes = Hero::with('Interview', 'Interview.stories', 'Interview.images')->orderBy('position', 'asc')->get();
Model Relationships:
Hero:
public function Interview()
{
return $this->hasOne('App\Interview', 'heroInt_id');
}
Interview:
public function relationships()
{
return $this->belongsTo('App\Hero');
}
public function stories()
{
return $this->hasMany('App\InterviewStory');
}
public function images()
{
return $this->hasMany('App\InterviewImage');
}
InterviewImage:
public function relationships()
{
return $this->belongsTo('App\Interview');
}
InterviewStory
public function relationships()
{
return $this->belongsTo('App\Interview');
}
Html:
Loop:
#foreach($heroes as $hero)
#foreach($hero->Interview as $story)
<div>{{$story->id}}</div>
#endforeach
#endforeach
You can't loop on Interview because this a hasOne relationship. What you might want to do is
#foreach($heroes as $hero)
#foreach($hero->interview->stories as $story)
<div>{{$story->id}}</div>
#endforeach
#endforeach
I think your problem is because not following laravel "assumes". In documentation:
Additionally, Eloquent assumes that the foreign key should have a
value matching the id column of the parent. In other words, Eloquent
will look for the value of the user's id column in the user_id column
of the Phone record. If you would like the relationship to use a value
other than id, you may pass a third argument to the hasOne method
specifying your custom key:
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
What is the id of Hero? If it's different than id, then you have to add the local_key name.

Resources