How do write this eloquent query in Laravel so that it eager loads with() the relationship model in this example between a User model and Profile model? I was trying to avoid 2 separate queries.
I feel I am close, but somethings not quite right.
$author = User::where('id', $id)->with('profile')->get();
The collection is returning the user details correctly. But it's showing the profile relationship as null.
#relations: array:1 [▼
"profile" => null
]
I believe I have things setup correctly with a User model and a Profile needed relationships.
User.php
public function profile()
{
return $this->hasOne('App\AuthorProfile', 'user_id');
}
AuthorProfile.php
public function user()
{
return $this->belongsTo('App\User');
}
Assuming for AuthorProfile model table you have record with id of user it should be fine.
However you wrote:
I was trying to avoid 2 separate queries.
Well, it's not true, if you have single record, eager loading won't help you at all. In this case 2 queries will be execute - no matter if you use eager loading or you won't.
Eager loading would help if you had multiple users and for each of them you wanted to load profile, but if you have single record it won't change anything.
Additionally instead of:
$author = User::where('id', $id)->with('profile')->get();
you should rather use:
$author = User::with('profile')->find($id);
because you expect here single user.
$users = User::with('profile')->find($id);
Your model should be like this.The User_id on the profile table and id on the user table
public function profile()
{
return $this->hasOne('App\AuthorProfile', 'user_id','id');
}
Related
I have a pivot table called invite_riskarea which is designed as follows:
This table handles the permissions that have a specific user (through an invite id) to access to specific riskfields. Each riskfield is associated with a riskarea which acts as the main container of specific riskfields.
Within the model Invite I have this relationship:
public function riskareas()
{
return $this->belongsToMany(Riskarea::class)->withPivot('riskfield_id', 'insert', 'edit', 'view');
}
In this way I can return all the riskareas associated with a specific invite, and I should be able to return all the riskfields associated with a specific riskarea in the same invite model.
As you can see from the table invite_riskarea, I have three columns called insert, edit, and delete. These columns manage the types of permissions assigned to a specific user (via an invite id) for a specific riskfield belonging to a riskarea.
I'm trying to retrieve the riskarea permission in the following way:
$invite = Invite::where('id', 58)->first();
$riskarea = $invite->riskareas[0];
$riskfield = $riskareas->riskfields[0];
echo 'view permission => ' . $riskfield->insert;
The problem's that I'm not able to setup a correct relationship in the Invite model that returns me the pivot data of the permissions columns only for the riskfield associated with the riskarea.
So I have manage to handle this situation in this way:
$riskareas = Riskarea::all();
foreach ($riskareas as &$riskarea) {
foreach ($riskarea->riskfields as &$riskfield) {
$result = DB::table('invite_riskarea')
->select('insert', 'edit', 'view')
->where([
'riskarea_id' => $riskarea->id,
'riskfield_id' => $riskfield->id
])
->first();
if ($result) {
$riskfield->insert = $result->insert;
$riskfield->edit = $result->edit;
$riskfield->view = $result->view;
}
}
}
Essentially, I get all the riskareas, and then I iterate over the riskfields associated. For each riskfield, I get the permissions in the invite_riskarea table and then I have the correct structure that I want.
So to summarize:
Is it actually possible create a model relationship that returns the permissions for riskfield and not for riskarea?
Is my table implementation good enough to handle that situation?
I suggest you define back the many-to-many relation for the Riskfield model with the Invite model.
You can also define a direct many-to-many relationship with riskfield in the Invite model. This is how convenient it is for you personally.
And so the inverse many-to-many relationship
public function invites()
{
return $this->belongsToMany(Invite::class)->withPivot('insert', 'edit', 'view');
}
Then get all objects' Riskfields that are associated with the specified invite:
$riskfields = Riskfields::wherehas('invites' . function (Builder $query) use ($invite_id) {
$query->where('invites.id', $invite_id);
})->with('invites')->get();
Then you can access the desired fields of the pivot table in the specified way:
foreach ($riskfields as $riskfield) {
foreach ($riskfield->invites as $invite) {
$insertRiskField = $invite->pivot->insert;
$editRiskField = $invite->pivot->edit;
$viewRiskField = $invite->pivot->view;
}
}
Eager loading executes one query to the database
Yes
Documentation Laravel
I have relationship batch and project
function project(){
return $this->hasMany(Project::class,'batch_id');
}
I need to get which batch is complete based on all projects (status = COMPLETED) as a Laravel accessor in model.
this is my sample data
please help. thanks!
You need to change the code from
function project(){
return $this->hasMany(Project::class,'batch_id');
}
to
function project(){
return $this->hasMany(Project::class,'project_id');
}
to get the completed batch
$batch=Project::where('status','COMPLETED')->pluck('batch_id');
In the Relationship
You can add the where inline on the relationship
function project(){
return $this->hasMany(Project::class,"bacth_id")->where("status","COMPLETED");
}
When Invoking the Relationship
If you want to keep the relationship without the where and only use it sometimes, you can call the relationship and add the where. for example, if you have an instance of your model (im going to call it $model in this example) you can do the following, notice the parenthesis next to the relationship name:
$completed = $model->project()->where("status","COMPLETED")->get();
In a Query
If you are writing a query where you are trying to pull in only versions of that model where the project is complete you can use the whereHas function. as i dont know what your model is called in your example, im just gonna call it Model
$posts = Model::whereHas("project", function (Builder $query) {
$query->where("status","COMPLETED");
})->get();
I have 2 tables called jobs & job_records. Its relationship as below:
JobRecords Model
public function job()
{
return $this->belongsTo(Job::class);
}
Job Model:
public function jobRecord()
{
return $this->hasOne(JobRecord::class);
}
jobs table has 2 columns that I need to display alongside my job_records table view. It's total_pges & status.
In my JobRecords Controller, I have tried the following method. It throws me an error of Call to undefined relationship.
JobRecordController:
$job_records = JobRecord::whereStatus('In Progress')
->with('jobs', 'jobs.status', 'jobs.total_pges')
->get();
return DataTables::of($job_records)
I am still beginning with Laravel and PHP. I can sense that there is something wrong with the relationship. But I couldn't figure out what it is exactly. Can anyone help me out with this matter?
In your JobRecord model change the relation ship as
public function job()
{
return $this->hasOne('App\Models\Job','foreign_key','local_key');
}
Similarly, in Job model
public function job()
{
return $this->belongsTo('App\Models\JobRecord','foreign_key','local_key');
}
Replace foreign_key and local_key with appropriate values...
I deleted my previous answer. What are you trying to do exactly? You can't use "jobs" in the "with function" without to define "jobs" as function in the model.
If you change it to "job" (instead of "jobs), then it would work, but I don't know if you want this. With your query you saying that a record have many jobs? But your model doesn't define that.
I have two table witch named users & Inbox
In the Inbox table I have a column named sender_id that have the user_id of the sender
I want to show this message in the view. I need a query to get the sender_id from the inbox table and use that to select a certain user from the users table
I need to do this with all messages and all users.
Laravel is basicly straith foward when you use eloquent. You can always customise it.
First, almost all the time, I create a model and a migration at the same time using this : php artisan make:model Something --migration
I know you already make some models and/or migrations, but I'll go step by step to help you understand it.
So, in your case, it'll be php artisan make:model User --migration and php artisan make:model Inbox --migration. Doing this, you get two model named User and Inbox and two migration named date_create_users_table.php and date_create_inboxs_table.php. Maybe you already did the default user table with php artisan make:auth. If it's the case, don't remake one.
I'm not sure about how laravel will name the Inbox model migration... Since, I think, Laravel 5.3, the plurialisation changed and don't always just add an "S" at the end.
Then, now you got your models and migrations, let's add some line into your migration files. Since you want to do a one to many relationship. You don't need to touch the user one. Only the Inbox migration. Each Inbox is related to one User and Users can have many Inboxs. Add something like this in your migration:
public function up()
{
Schema::create('inboxs', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->foreign('user_id')->references('id')->on('users');
all other columns...
});
}
There, you can change the column's name if you need to have a sender, a recipient, etc... Do this instead :
public function up()
{
Schema::create('inboxs', function (Blueprint $table) {
$table->increments('id');
$table->integer('sender_id');
$table->foreign('sender_id')->references('id')->on('users');
$table->integer('recipient_id');
$table->foreign('recipient_id')->references('id')->on('users');
all other columns...
});
}
What we just did, it's creating the Foreign key that Laravel will use to build the query. There is one last part before the fun one. We need to create the relation in our Model. Begin with the user one:
App/User.php
public function inboxs() {
return $this->hasMany(Inbox::class);
}
And now into the App/Inbox.php model:
public function user() {
return $this->belongsTo(User::class);
}
If you need to have a Sender/Recipient/etc... go this way instead:
public function sender() {
return $this->belongsTo(User::class);
}
public function recipient() {
return $this->belongsTo(User::class);
}
Note that each of your function need to be writen in the same way it's into your migration. sender_id need a relation named sender().
Now, that our relations are done, we can simply call everything using eloquent.
$inboxs = Inbox::with('sender')->get();
This will return an array of all your Inbox into the inboxs table. You can access the sender this way: $inboxs[0]->sender();
You need the id, do this: $sender_id = $inboxs[0]->sender_id;
The sender name : $sender_name = $inboxs[0]->sender->name;
If you want to get one Inbox and you have the id, just do this $inbox = Inbox::with('sender')->find($id);
This way you don't get an array, only one result and can access the sender directly using $sender_name = $inbox->sender->name; instead of having to add [0] or using a foreach loop.
You can get all messages sended by a user using something like this:
$inboxs = Inbox::where('sender_id', $sender_id)->get();
Finally, you can pass your data to the view using:
return view('path.to.view')->with('inbox',$inbox);
Into the view you do this to show the sender's name:
//If view.blade.php
{{$inbox['sender']['name']}} //work a 100%
{{$inbox->sender->name}} //I'm not sure about this one
//If not using blade
<?php echo $inbox['sender']['name']; ?>
There is a lot of thing you can do using Eloquent and you can add as much condition you want. The only thing I suggest you to really do if you want to use Eloquent, be aware about the n+1 problem. There is a link where I explain it. Look for the EDIT section of my answer.
If you need some documentation:
Laravel 5.3 Relationships
Laravel 5.3 Migrations
Laravel 5.3 Eloquent
I think you should update your code like:
$user_messages = DB::table('messages')
->select('messages.id as msgId','messages.message as message','users.id as userId','users.user_name as user_name')
->join('messages','messages.user_id','=','users.id')
->where('messages.user_id',$user_id)
->get();
return view("view.path")
->with('messages',$user_messages);
Hope this work for you!
In Model :
namespace App;
use Illuminate\Database\Eloquent\Model;
class Messages extends Model
{
protected $table = 'table_name';
public function sender()
{
return $this->belongsTo('App\User', 'sender_id', 'id');
}
}
In Controller :
public function functionName($user_id){
$messages = Messages::where('sender_id', $user_id)->get();
return view("view.path")
->with('messages',$messages);
}
In view, you can access seder details like this $message->sender->name for name for id $message->sender->id
Yii2 build relation many to many
I have 2 tables users and friends
Code query
$friends = Friends::find()
->select(['friends.user_id', 'users.name'])
->leftJoin('users','users.id = friends.friend_user')
->with('users')
->all();
In result error
Invalid Parameter – yii\base\InvalidParamException. app\models\Friends has no relation named "users".
Friends has a column called user_id and thus only belongs to one user. If you auto-generated the Friends ActiveRecord it probably has a function getUser (singular because it is only one) that will look something like this:
public function getUser() {
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
So you're getting the error because no getUsers function exists (that returns a valid ActiveQuery object). Because there can only be one user per friend I think you should use the singular version. And if that still gives the same error you should implement the function above and maybe change it a bit to match your classname.
When you use with(['relation']) to load relations Yii will convert the entry to getRelation and call that function on the model to get the query that is needed to load the relation.