How to query relationships in Laravel 4 with Eloquent? - codeigniter

Coming from CodeIgniter's Datamapper ORM I am still trying to get my head around Laravel's Eloquent ORM.
Given the fact that I have an ACCOUNT and a USER table (simplified):
ACCOUNT
- id
- name
USER
- id
- account_id
- username
One account has many users. One user belongs to one account. So we're dealing with a one-to-many relationship. Everything is already set-up in the models.
In CodeIgniter's Datamapper I would have done the following to get the user from any given ID and at the same time check if that user is related to the current account:
$u = new User();
$u->where('username', $username);
$u->where_related_account('id', $account_id);
$u->get();
if ( ! $u->exists()) exit; // or do something...
// otherwise continue to use the "$u" user object
This syntax is very logical and easy to understand. In Eloquent I have a hard time to achieve the same with a similar easy syntax. Does anyone have any suggestions?

Very simply (ignoring the relationship between the user and the account), it could just be:
$u = User::where('username', $username)
->where('account_id', $id)
->get();
That will return you your user's details.
Otherwise, assuming that you have your User and Account classes and DB tables are set up correctly (as per the Laravel docs), you should be able to just do:
$user_exists = Account::find($account_id)
->users()
->where("username", "=", $username)
->first()
->exists;
if ($user_exists)
{
doThings();
}

If you've correctly set up your models and database tables (as #msturdy said) you should actually be able to return your user account by simply going:
$user = User::whereUsername($username)
->first(); // or User::where('username', $username)->first();
if ($user) {
$account = $user->accounts()
->whereId($account_id)
->first(); // or $user->accounts()->where('id', $account_id)->first();
}
This gives you the ability to access the user and account models
you could even extend your User model to include the following methods:
class User extends Eloquent {
...
public static function byUsername($username) {
return static::whereUsername($username)->first();
}
public function getAccount($id) {
return $this->accounts()->whereId($id)->first();
}
...
}
and then simply go
$user = User::byUsername($username);
if ($user) {
$account = $user->getAccount($account_id);
}
which might be better for you if you are using the code in multiple controllers.

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

get user from belongsToMany relationships

I have a service model in October CMS.
In this model, I need to get postman's users (postman is user group) but I am receiving this error:
Trying to get property of non-object
This is my code
public function getPostmanIdOptions()
{
$groups = UserGroup::where('id','4')->lists('name', 'id');
$groups->users;
$list = [' ' => 'choose'] + $groups;
return $list;
}
At the moment, your lists() function will only return the name and the id of each user group. This is used to return thelselect options for the backend select (I am assuming).
What you need to do in this case is return the record based on its id which can be done using the find() eloquent method.
By doing this, the full UserGroup model will be returned, with it's relationships etc.
You're new code should look something like this:
...
$group = UserGroup::find(4);
$users = $group->users;
...
After retrieving the users, you can then using the lists() method if required to:
$list = $group->users->lists('name', 'id');

Relationship with user condition

I have a follow system that works like Twitter's follow (you follow other people and get updates from them).
I have this relationship set in my User.php model:
public function follows()
{
return $this->hasMany('App\Follow');
}
This obviously gets all records of users who follow this person.
However, when displaying the person's profile page, I want to get a relationship that checks if a follow record exists given the user ID.
Something like:
public function userFollow()
{
return $this->hasOne('App\Follow')->where('user_id', Auth::user()->id);
}
So now my profile page query looks like this:
$user = User::where('username', $username)
->with('userFollow')
->first();
This works as long as the user is logged in. If the user isn't logged in, the page gives me this error:
ErrorException in User.php line 28: Trying to get property of
non-object
How do I fix this? What is the proper way of using conditions in relationships?
This is because if user is logged out then Auth::user() is null, because user session has ended. Auth::user() is no more object, so you can't get the id from a null object.
Here you can do something like that
public function userFollow()
{
$userId = Auth::user() ? Auth::user()->id : $this->id;
return $this->hasOne('App\Follow')->where('user_id', $userId);
}
then it will not look for authenticated user, it will just get the particular user table's id.
The proper way of using conditions in relationships is using Constraining eager loads
public function userFollow()
{
return $this->hasOne('App\Follow');
}
And,
if(Auth::check())
{
$user = User::where('username', $username)
->with(['userFollow'=>function($query)
{
$query->where('user_id', Auth::user()->id);
}]);
->first();
}

When to refresh an instance using Eloquent

Why do we have to refresh an instance after adding new relations?
Example, theres a pivot table 'favourites' which stores all the favourite relation N:N between items and users:
$item = factory(App\Item::class)->create();
$user = factory(App\User::class)->create();
$user->setFavourite($item->id);
dd($user->favourites);//is Empty!!
//whereas refreshing the instance...
$user = $user->fresh();
dd($user->favourites);//is the corresponding collection within the item
The setFavourite method is simply an attach/detach of the relationship:
public function setFavorito($id)
{
if(Auth::check())
{
$user = Auth::user();
if($user->esAnuncioFavorito($id)) {
$user->favoritos()->detach($id);
}else{
$user->favoritos()->attach($id);
}
}
return Redirect::back();
}
I though that when eloquent calls for the related instances of an instance it was done at the moment, therefore a call to the DB shall be done and would end retrieving an updated collection including the added one.
Could someone explain it to me? When should I use the fresh() method?

Laravel Counting and retrieving many to many relationships

I have a many to many Laravel Relationship Many Users have Many Roles
I know that I can do User::find(1)->roles()->get() to get all roles with an ID of 1 for a User but this requires me to know the ID of the role as oppossed to just its names
What sort of eloquent query could I put in my user Repository to do something like
public function getAllUsersWithRole($roleType){
//Query goes here
}
Where $roleType is the name of the Role. So I need to look up the ID of the role based on hte name and then return users that have that role ID in the pivot table
does this look like what you want:
public function getAllUsersWithRole($roleType){
$role= Role::where('name', $roleType)->first();
$users= $role->users;
return $users;
}
If you have correct relation definitions in your Eloquent models you should be able to get the users by the role name this way:
$roleType = 'YourRoleName';
$users = Role::whereType($roleType)->first()->users;
Grab the users, then get the roles for all the users.
public function getAllUsersWithRole($roleType){
$users = User::where('role_type', '=', $roleType)->get();
$roles = $users->roles()->get();
return $roles;
}

Resources