Laravel: Get users based on Eloquent relation - php

I have a User model which has an attribute type among other attributes. Type is used to identify parents and children.
Parent and children (students) have many-to-many relationship.
Also students belong to one or many groups (model Group).
User model
/**
* Filter the scope of users to student type.
*
* #param $query
*/
public function scopeStudent($query){
$query->where('type', '=', 'std');
}
/**
* Filter the scope of users to parent type.
*
* #param $query
*/
public function scopeParent($query){
$query->where('type', '=', 'prt');
}
/**
* List of groups the user is associated with.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function groups(){
return $this->belongsToMany('\App\Group', 'group_user_assoc')
->withTimestamps();
}
/**
* List of students associated with the user(parent).
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function children(){
return $this->belongsToMany('\App\User', 'student_parent_assoc', 'parent_id', 'student_id')
->withPivot('relation');
}
/**
* List of parents associated with the user(student).
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function parents(){
return $this->BelongsToMany('\App\User', 'student_parent_assoc', 'student_id', 'parent_id')
->withPivot('relation');
}
The aforementioned relations are working correctly.
Below are my association tables.
student_parent_assoc
----------------------
+------------+------------------+------+-----+
| Field | Type | Null | Key |
+------------+------------------+------+-----+
| student_id | int(10) unsigned | NO | PRI |
| parent_id | int(10) unsigned | NO | PRI |
| relation | varchar(25) | YES | |
+------------+------------------+------+-----+
group_user_assoc
----------------------
+------------+------------------+------+-----+
| Field | Type | Null | Key |
+------------+------------------+------+-----+
| group_id | int(10) unsigned | NO | MUL |
| user_id | int(10) unsigned | NO | MUL |
| created_at | timestamp | NO | |
| updated_at | timestamp | NO | |
+------------+------------------+------+-----+
I need to find students who do not belong to any group along with their parents. I have managed to find students like so
$students = User::student()
->whereDoesntHave('groups')
->get();
Question:
Now I want to find parents of these students. But I am not able to build an eloquent query for the same. How do I do it?
Note: I could get collection of students from above query and run a foreach loop to get their parents like so
$parents = new Collection;
foreach($students as $student) {
foreach($student->parents as $parent) {
$parents->push($parent);
}
}
$parents = $parents->unique();
But I need a Builder object and not a Collection as I am using Datatables server side processing with Yajra/datatables.

for loading parents relation you hae to use eager loading.
2 methods are with($relation) and load($relation). Difference is just you get parents already with result objects or load them later.
So in your example to get parents you can use with('parents') or if you want to modify resulted set:
User::student()
->with(['parents' => function ($parentsQueryBuilder) {
$parentsQueryBuilder->where('condition', 1)->whereIn('condition2', [1,2,3]);
}])
->whereDoesntHave('groups')
->get();
Then you will get your parents in a relationship aswell but performance will be high cause you will spend only one query to load parents to your objects. Then you can pluck if needed them in one collection like this:
$students->pluck('parents')->flatten()->unique();
Or example 2 - if you just need all parents related to selected students - almost the same what eager loading does:
$studentIds = $students->modeKeys();
User::parent()->whereHas('children', function ($query) use($studentIds) {
$query->whereIn('id', $studentIds);
})->get();
UPDATED
For getting builder of parents try this:
/** BelongsToMany <- relation query builder */
$relation = with(new User)->query()->getRelation('parents');
$relation->addEagerConstraints($students->all());
This will create for you new instance of BelongsToMany relation and attach Constraints of whereIn($studentIds) to it. Then hitting ->get() on it you have to receive related $parents

Well, I managed to solve it like so
$students = User::student()
->with('parents')
->whereDoesntHave('groups')
->has('parents') //to get only those students having parents
->get();
$parent_ids = array();
// get ids of all parents
foreach ($students as $student) {
foreach ($student->parents as $parent) {
$parent_ids[] = $parent->user_id;
}
}
// build the query
$users = User::parent()
->with('children')
->whereIn('user_id', $parent_ids);
I would still like it if someone could suggest a better and simple approach.

Related

Laravel/Eloquent ORM - retrieve only referenced records

I have a many-to-many relationship which I resolved with an intersection table.
So table A and B are connected through AxB.
A and AxB are 1-n and B and AxB are 1-n.
The actual names of the tables:
table A = extensiontables_registry
table B = ad_groups
table AxB = extensiontables_registryxad_groups
You can see the logical datamodel here:
https://imgur.com/MNpC3XV
Ive put the part we are talking about right now into a red frame.
Now, I have the following line of code in my backend-API:
$permittedTables = extensiontables_registry::findMany($ids)->pluck('extensiontable_name')->toArray();
To keep things short, the $ids contains all the ids from "ad_groups". These I've gotten from a fetch which works as intended.
The $ids contains these values/ids according to my logs:
[1,2,3,4,5,6,7,8,9,10,12]
Now, the extensiontables_registryxad_groups currently looks like this:
select * from extensiontables_registryxad_groups;
+-----------------------------+-------------+------------+------------+
| extensiontables_registry_id | ad_group_id | created_at | updated_at |
+-----------------------------+-------------+------------+------------+
| 1 | 8 | NULL | NULL |
| 2 | 8 | NULL | NULL |
+-----------------------------+-------------+------------+------------+
2 rows in set (0.000 sec)
And the extensiontables_registry looks like this:
+----+-----------------------+------------+------------+
| id | extensiontable_name | created_at | updated_at |
+----+-----------------------+------------+------------+
| 1 | extensiontable_itc | NULL | NULL |
| 2 | extensiontable_sysops | NULL | NULL |
| 3 | test | NULL | NULL |
+----+-----------------------+------------+------------+
And now the problem is that my codesnippet from above:
$permittedTables = extensiontables_registry::findMany($ids)->pluck('extensiontable_name')->toArray();
returns me this result:
array (
0 => 'extensiontable_itc',
1 => 'extensiontable_sysops',
2 => 'test',
)
So the codesnippet does NOT do what I want it to do. It should only fetch me the names of those extensiontables which have IDs which exist on the very same record(s) in extensiontables_registryxad_groups with IDs from my inputarray above. So The result I currently would expect would be this:
array (
0 => 'extensiontable_itc',
1 => 'extensiontable_sysops'
)
I am pretty new to laravel and eloquent, so I dont really know what I did wrong in my codesnippet. I also have no idea what I can do to get this working as intended ^^
For the sake of completeness, I'll show you my eloquent models/classes for this arrangement of tables, just in case you might need it:
AD_Group.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ad_group extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name'
];
/**
* Hides pivot from return queries.
*
* #var array
*/
protected $hidden = [
'pivot'
];
/**
* Many-To-Many relationship with User-Model.
*/
public function Ad_users()
{
return $this->belongsToMany('App\Ad_user', 'Ad_usersxad_groups', 'Ad_group_id', 'Ad_user_id');
}
public function extensiontables()
{
return $this->belongsToMany('App\extensiontables_registry', 'extensiontables_registryxad_groups');
}
}
extensiontables_registry.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Extensiontables_Registry extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'Extensiontables_Registry';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'extensiontable_name'
];
/**
* Many-To-Many relationship with User-Model.
*/
public function Ad_groups()
{
return $this->belongsToMany('App\Ad_group', 'extensiontables_registryxad_groups');
}
}
I would be really thankful if you could help me or at least give me a hint what to look out for in order to find information on leveraging the methods of laravel/eloquent accordingly ^^ If you need any more info, ask right away :)
I think you got things a little mixed up.
what this query does:
$permittedTables = extensiontables_registry::findMany($ids)->pluck('extensiontable_name')->toArray();
is access the extensiontables_registry table and get records with the same array of $ids you sent.
But it will NOT take in consideration the relationship with extensiontables_registryxad_groups table because you didn't explicitly specify it in the query.
What you should add is has('relationship name') which get the records of this model that only has a corresponding Foreign key in relationship name' in your case Ad_groups
So in this case the query would look like this:
$permittedTables = extensiontables_registry::has('Ad_groups')->pluck('extensiontable_name')->toArray();
i think you can get there using join:
$permittedTables = extensiontables_registry::whereIn('id', $ids)
->join('extensiontables_registryxad_groups','extensiontables_registryxad_groups'.'extensiontables_registry_id','extensiontables_registry.id')
->select('extensiontables_registry.extensiontable_name')->get()->all();
please note you can avoid repeated results using 'distinct';

Laravel Eloquent Inner Join on Self Referencing Table

I'm trying to inner join a users table to itself using an eloquent model. I've looked everywhere but can't seem to find a solution to this without creating two queries which is what I am currently doing.
A users table has a many to many relationship itself through the pivot table friends
I tried and failed inner joining Users::class to itself. The best I can get at an inner join is by running two queries and seeing if there is an overlap. Thus one person has reached out to the other and vice versa.
friends | users
----------|------
send_id | id
receive_id| name
is_blocked|
sample data & expected result
users.id | name
---------|------
1 | foo
2 | bar
3 | baz
friends
send_id | receive_id | is_blocked
--------|------------|-----------
1 | 2 | 0
2 | 1 | 0
1 | 3 | 0
3 | 1 | 1
2 | 3 | 0
The user should have an eloquent relationship called friends. It should be what you expect comes out of requestedFriends or receivedFriends just joined.
foo->friends
returns `baz`
bar->friends
returns `foo`
baz->friends
returns empty collection
currently using
// User.php
public function requestedFriends()
{
$left = $this->belongsToMany(User::class, 'friends','send_id','receive_id')
->withPivot('is_blocked')
->wherePivot('is_blocked','=', 0)
->withTimestamps();
return $left;
}
public function receivedFriends()
{
$right = $this->belongsToMany(User::class, 'friends','receive_id','send_id')
->withPivot('is_blocked')
->wherePivot('is_blocked','=', 0)
->withTimestamps();
return $right;
}
public function friends()
{
$reqFriends = $this->requestedFriends()->get();
$recFriends = $this->receivedFriends()->get();
$req = explode(",",$recFriends->implode('id', ', '));
$intersect = $reqFriends->whereIn('id', $req);
return $intersect;
}
Research so far
Laravel Many to many self referencing table only works one way -> old question, but still relevant
https://github.com/laravel/framework/issues/441#issuecomment-14213883 -> yep, it works… but one way.
https://laravel.com/docs/5.8/collections#method-wherein
currently the only way I have found to do this in eloquent.
https://laravel.com/docs/5.7/queries#joins -> Ideally I would find a solution using an innerjoin onto itself, but no matter which way I put the id's I couldn't get a solution to work.
A solution would
A solution would inner join a self referencing table using eloquent in laravel 5.7 or 5.8, where a relationship only exists if send_id & receive_id are present on multiple rows in the friends table.
OR
Somehow let the community know that this can't be done.
Thanks in advance!
I have not checked this solution in every detail yet, but I have written a "ManyToMany" Class extending the "BelongsToMany" Class shipped with laravel, which appears to work.
The class basically just overrides the "get" method, duplicating the original query, "inverting" it and just performing a "union" on the original query.
<?php
namespace App\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class ManyToMany extends BelongsToMany
{
/**
* Execute the query as a "select" statement.
*
* #param array $columns
* #return \Illuminate\Database\Eloquent\Collection
*/
public function get($columns = ['*'])
{
// duplicated from "BelongsToMany"
$builder = $this->query->applyScopes();
$columns = $builder->getQuery()->columns ? [] : $columns;
// Adjustments for "Many to Many on self": do not get the resulting models here directly, but rather
// just set the columns to select and do some adjustments to also select the "inverse" records
$builder->addSelect(
$this->shouldSelect($columns)
);
// backup order directives
$orders = $builder->getQuery()->orders;
$builder->getQuery()->orders = [];
// clone the original query
$query2 = clone($this->query);
// determine the columns to select - same as in original query, but with inverted pivot key names
$query2->select(
$this->shouldSelectInverse( $columns )
);
// remove the inner join and build a new one, this time using the "foreign" pivot key
$query2->getQuery()->joins = array();
$baseTable = $this->related->getTable();
$key = $baseTable.'.'.$this->relatedKey;
$query2->join($this->table, $key, '=', $this->getQualifiedForeignPivotKeyName());
// go through all where conditions and "invert" the one relevant for the inner join
foreach( $query2->getQuery()->wheres as &$where ) {
if(
$where['type'] == 'Basic'
&& $where['column'] == $this->getQualifiedForeignPivotKeyName()
&& $where['operator'] == '='
&& $where['value'] == $this->parent->{$this->parentKey}
) {
$where['column'] = $this->getQualifiedRelatedPivotKeyName();
break;
}
}
// add the duplicated and modified and adjusted query to the original query with union
$builder->getQuery()->union($query2);
// reapply orderings so that they are used for the "union" rather than just the individual queries
foreach($orders as $ord)
$builder->getQuery()->orderBy($ord['column'], $ord['direction']);
// back to "normal" - get the models
$models = $builder->getModels();
$this->hydratePivotRelation($models);
// If we actually found models we will also eager load any relationships that
// have been specified as needing to be eager loaded. This will solve the
// n + 1 query problem for the developer and also increase performance.
if (count($models) > 0) {
$models = $builder->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
}
/**
* Get the select columns for the relation query.
*
* #param array $columns
* #return array
*/
protected function shouldSelectInverse(array $columns = ['*'])
{
if ($columns == ['*']) {
$columns = [$this->related->getTable().'.*'];
}
return array_merge($columns, $this->aliasedPivotColumnsInverse());
}
/**
* Get the pivot columns for the relation.
*
* "pivot_" is prefixed ot each column for easy removal later.
*
* #return array
*/
protected function aliasedPivotColumnsInverse()
{
$collection = collect( $this->pivotColumns )->map(function ($column) {
return $this->table.'.'.$column.' as pivot_'.$column;
});
$collection->prepend(
$this->table.'.'.$this->relatedPivotKey.' as pivot_'.$this->foreignPivotKey
);
$collection->prepend(
$this->table.'.'.$this->foreignPivotKey.' as pivot_'.$this->relatedPivotKey
);
return $collection->unique()->all();
}
}
I came across the same problem quite some time ago and have thus been following this problem closely and have made a lot of research. I have come across some of the solutions you have also found, and some more, and also have thought of other solutions that I summed here, mostly how to get both user_ids in the same column. I am afraid they will all not work well. I am also afraid that using any custom classes will stop you from using all of Laravel's handy relation features (especially eager loading). So I still thought what one could do, and, until one comes up with a hasMany-function on many columns, I think I have come up with a possible solution yesterday. I will show it first and then apply it to your project.
My project
Initial solution
In my project, one user partners with another one (= partnership) and then later will be assigned a commission. So I had the following tables:
USERS
id | name
---------|------
1 | foo
2 | bar
17 | baz
20 | Joe
48 | Jane
51 | Jim
PARTNERSHIPS
id | partner1 | partner2 | confirmed | other_columns
----|-----------|-----------|-----------|---------------
1 | 1 | 2 | 1 |
9 | 17 | 20 | 1 |
23 | 48 | 51 | 1 |
As each user should always have only one active partnership, the non-active being soft-deleted, I could have helped myself by just using the hasMany function twice:
//user.php
public function partnerships()
{
$r = $this->hasMany(Partnership::class, 'partner1');
if(! $r->count() ){
$r = $this->hasMany(Partnership::class, 'partner2');
}
return $r;
}
But if I had wanted to lookup all partnerships of a user, current and past, this of course, wouldn't have worked.
New solution
Yesterday, I came up with the solution, that is close to yours, of using a pivot table but with a little difference of using another table:
USERS
(same as above)
PARTNERSHIP_USER
user_id | partnership_id
--------|----------------
1 | 1
2 | 1
17 | 9
20 | 9
48 | 23
51 | 23
PARTNERSHIPS
id | confirmed | other_columns
----|-----------|---------------
1 | 1 |
9 | 1 |
23 | 1 |
// user.php
public function partnerships(){
return $this->belongsToMany(Partnership::class);
}
public function getPartners(){
return $this->partnerships()->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}])->get();
}
public function getCurrentPartner(){
return $this->partnerships()->latest()->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}])->get();
}
// partnership.php
public function users(){
return $this->belongsToMany(User::class);
}
Of course, this comes with the drawback that you always have to create and maintain two entrances in the pivot table but I think this occasional extra load for the database -- how often will this be altered anyway? -- is preferable to having two select queries on two columns every time (and from your example it seemed that you duplicated the entries in your friends table anyway).
Applied to your project
In your example the tables could be structured like this:
USERS
id | name
---------|------
1 | foo
2 | bar
3 | baz
FRIENDSHIP_USER
user_id | friendship_id
---------|------
1 | 1
2 | 1
3 | 2
1 | 2
FRIENDSHIPS
id |send_id* | receive_id* | is_blocked | [all the other nice stuff
--------|---------|-------------|------------|- you want to save]
1 | 1 | 2 | 0 |
2 | 3 | 1 | 0 |
[*send_id and receive_id are optional except
you really want to save who did what]
Edit: My $user->partners() looks like this:
// user.php
// PARTNERSHIPS
public function partnerships(){
// 'failed' is a custom fields in the pivot table, like the 'is_blocked' in your example
return $this->belongsToMany(Partnership::class)
->withPivot('failed');
}
// PARTNERS
public function partners(){
// this query goes forth to partnerships and then back to users.
// The subquery excludes the id of the querying user when going back
// (when I ask for "partners", I want only the second person to be returned)
return $this->partnerships()
->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}]);
}

A little complex query using eloquent

I am trying to learn laravel and currently using eloquent to interact with the database. I am stuck on how I could use eloquent to get a kind of a join in eloquent.
I have a many to many relation between two tables :users and projects , I use sharedProject table to be the intermediate table .
The tables are as such
Users:
| iduser | name | password |
----------------------------------------
| 1 | somename | hashPassword |
| 2 | somename2 | hashPassword |
| 3 | somename3 | hashPassword |
| 4 | somename4 | hashPassword |
----------------------------------------
Projects:
| pid | projectname
-------------------
| 1 | somename
| 2 | somename
SharedProjects:
| pid | iduser | sharedProjectid |
----------------------------------
| 1 | 1 | 1 |
| 1 | 2 | 2 |
Now I want to get all the users who are not sharing a given project, for example in the above case for project with id 1 , I should get user 3 and user 4.
Here are my relationships in User model
/**
* User can have many projects
*
* #var array
*/
public function projects(){
return $this->hasMany('App\Project','pid','iduser'); // hasmany(model,foreignkey,localkey)
}
/**
* The user can have many shared projects
*/
public function sharedProjects()
{
return $this->belongsToMany('App\SharedProject', 'sharedProjects', 'iduser', 'pid');// belongsToMany('intermediate tablename','id1','id2')
}
and in the Project model:
/**
* The project can be shared by many users
*/
public function sharedProjects()
{
return $this->belongsToMany('App\SharedProject', 'sharedProjects', 'pid', 'iduser');// belongsToMany('intermediate tablename','id1','id2')
}
/**
* a project belongs to a single user
*
* #var array
*/
public function user()
{
return $this->belongsTo('App\User');
}
I would prefer a eloquent way to do this , but would also except it, if can't be done in eloquent and I have to see a alternate approach I would appreciate a plain mysql query as well.
Thanks
Once you define your Eloquent models and your many-to-many relationships, you can use them to get the data you're looking for.
Assuming a User model that has a projects relationship defined, you can use the whereDoesntHave() method to query for a list of users that are not related to a specific project.
$projectId = 1;
$users = User::whereDoesntHave('projects', function ($q) use ($projectId) {
return $q->where('projects.id', $id);
})->get();
You can read about defining many-to-many relationships in Eloquent here.
You can read about querying relationship existence here.
As you may notice, not all methods are documented (like whereDoesntHave()), so you may have to go source code diving. You can dig into the Eloquent codebase here.
I resort to use plain mysql queries, this seems to work for me:
$nonSharedUsers = DB::select( DB::raw("SELECT iduser FROM users WHERE NOT EXISTS (SELECT * FROM sharedProjects WHERE sharedProjects.iduser= users.iduser and sharedProjects.pid=:projectId)"), array(
'projectId' => $pid,
));

Query multiple, nested Relationships in Laravel/Eloquent

I'm trying to build a simple news feed for posts in Laravel with Eloquent.
I basically want to retrieve all Posts...
where I am author
where people I follow are author (followable)
where people I follow have commented on
where people with same field_id are author
where poeple with same school_id are author
in one query.
As I never worked intensivly with joined/combined SQL queries, any help on this is greatly appreciated!
My Tables
users table
+----+
| id |
+----+
posts table
+----+-----------+-------------+
| id | author_id | author_type |
|----|-----------|-------------|
| | users.id | 'App\User' |
+----+-----------+-------------+
comments table
+----+----------------+------------------+-----------+-------------+
| id | commentable_id | commentable_type | author_id | author_type |
|----|----------------|------------------|-----------|-------------|
| | posts.id | 'App\Post' | users.id | 'App\User' |
+----+----------------+------------------+-----------+-------------+
schoolables table
+---------+-----------+----------+
| user_id | school_id | field_id |
+---------+-----------+----------+
followables table
+-------------+---------------+---------------+-----------------+
| follower_id | follower_type | followable_id | followable_type |
|-------------|---------------|---------------|-----------------|
| users.id | 'App\User' | users.id | 'App\User' |
+-------------+---------------+---------------+-----------------+
My Models
class Post extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function author()
{
return $this->morphTo();
}
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Comment extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function author()
{
return $this->morphTo();
}
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function commentable()
{
return $this->morphTo();
}
}
class User extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function posts()
{
return $this->morphMany(Post::class, 'author')->orderBy('created_at', 'desc');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function comments()
{
return $this->morphMany(Comment::class, 'author')->orderBy('created_at', 'desc');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function schoolables()
{
return $this->hasMany(Schoolable::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function following()
{
return $this->morphMany(Followable::class, 'follower');
}
}
You can try to use left joins for this query, but it would become a complex thing, because you have to make all the joins with leftJoins and then a nested orWhere clause on all the
$posts = Post::leftJoin('..', '..', '=', '..')
->where(function($query){
$query->where('author_id', Auth::user()->id); // being the author
})->orWhere(function($query){
// second clause...
})->orWhere(function($query){
// third clause...
.....
})->get();
I don't think this will be manageable, so I would advice using UNIONS, http://laravel.com/docs/5.1/queries#unions
So it would be something like..
$written = Auth::user()->posts();
$following = Auth::user()->following()->posts();
After getting the different queries, without getting the results, you can unite them..
$posts = $written->union($following)->get();
Hopefully this will direct you in the right direction
Okay, so you want to retrieve all posts where any of the 5 conditions above apply. The trick to writing such queries is to break them up into smaller, more manageable pieces.
$query = Post::query();
So let's say you are $me.
The ids of users you are following can be obtained with
$followingUserIds = $me
->following()
->where('followable_type', User::class)
->lists('followable_id');
The ids of users in the same fields as you can be obtained with
$myFieldIds = $me->schoolables()->lists('field_id');
$sharedFieldUserIds = Schoolable::whereIn('field_id', $myFieldIds)->lists('user_id');
Similarly, users in the same school as you can be obtained with
$mySchoolIds = $me->schoolables()->lists('school_id');
$sharedSchoolUserIds = Schoolable::whereIn('school_id', $mySchoolIds)->lists('user_id');
Let's define each of those conditions:
Where I am author
$query->where(function($inner) use ($me) {
$inner->where('posts.author_type', User::class);
$inner->where('posts.author_id', $me->id);
});
Where people I follow are author (followable)
$query->orWhere(function($inner) use ($followingUserIds) {
$inner->where('posts.author_type', User::class);
$inner->whereIn('posts.author_id', $followingUserIds);
});
where people I follow have commented on
This one is actually slightly tricky: we need to use the ->whereHas construct, which finds posts with at least 1 comment matching the subquery.
$query->orWhereHas('comments', function($subquery) use ($followingUserIds) {
$subquery->where('comments.author_type', User::class);
$subquery->whereIn('comments.author_id', $followingUserIds);
});
The remaining two are simple.
where people with same field_id are author
$query->orWhere(function($inner) use ($sharedFieldUserIds) {
$inner->where('posts.author_type', User::class);
$inner->whereIn('posts.author_id', $sharedFieldUserIds);
});
and you can see where this is going
where poeple with same school_id are author
$query->orWhere(function($inner) use ($sharedSchoolUserIds) {
$inner->where('posts.author_type', User::class);
$inner->whereIn('posts.author_id', $sharedSchoolUserIds);
});
To get the matching posts, you just need to do
$posts = $query->get();
While constructing the query from scratch works in this particular case, it will create a fairly brittle structure if your requirements ever change. For added flexibility, you probably want to build query scopes on the Post and Comments models for each of those components. That will mean that you only need to figure out one time what it means for a post to be authored by someone a user follows, then you could simply do Post::authoredBySomeoneFollowedByUser($me) to get the collection of posts authored by someone you follow.

Obtain three level relationship on Laravel 5

I'm trying to obtain a three level relationship data, but I'm lost about it using laravel 5.1
I'll try to explain my scenario, hope you can help me.
I've got two models called Host and User.
This models are grouped by Hostgroup and Usergroup models, using a Many To Many relationship.
Then I've got a table called usergroup_hostgroup_permissions which relation an Usergroup with a Hostgroup:
+--------------+----------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------------------+------+-----+---------------------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| usergroup_id | int(10) unsigned | NO | MUL | NULL | |
| hostgroup_id | int(10) unsigned | NO | MUL | NULL | |
| action | enum('allow','deny') | NO | | allow | |
| enabled | tinyint(1) | NO | | 1 | |
+--------------+----------------------+------+-----+---------------------+----------------+
I'd like to obtain a list of users that belongs to an usergroup with a relation in this table with a hostgroup where my host belongs to.
For example:
My host_1 belongs to DEV_servers.
On usergroup_hostgroup_permissions table, there's an entry that allows developers to access to DEV_servers.
The usergroup developer has 3 users user_1, user_2 and user_3.
Any hint? Thanks in advance!
In order to obtain a list of users in a particular host, you need to nest all the underlying relationships (via .) using a whereHas method on the User model. i.e.
$users = User::whereHas('usergroup.hostgroups.hosts', function($q) use ($hostID){
$q->where('id', $hostID);
})->get();
Moreover, if you want to check against whether the user is allowed to access that particular host, then you may chain another where() to the above as such:
$users = User::whereHas('usergroup.hostgroups.hosts', function($q) use ($hostID){
$q->where('id', $hostID)->where('usergroup_hostgroup_permissions.action', 'allow');
})->get();
Note: If you are warned on an ambiguous id field, try including the table hosts to which the id belongs to as well, i.e. hosts.id.
I am assuming that you have defined the relations for those models as follows:
class HostGroup extends Eloquent {
/** ...
*/
public function hosts(){
return $this->hasMany('App\Host');
}
public function usergroups(){
return $this->belongsToMany('App\UserGroup', 'usergroup_hostgroup_permissions');
}
}
class Host extends Eloquent {
/** ...
*/
public function hostgroup() {
return $this->belongsTo('App\HostGroup');
}
}
class UserGroup extends Eloquent {
/** ...
*/
public function users(){
return $this->hasMany('App\User');
}
public function hostgroups(){
return $this->belongsToMany('App\HostGroup', 'usergroup_hostgroup_permissions');
}
}
class User extends Eloquent {
/** ...
*/
public function usergroup(){
return $this->belongsTo('App\UserGroup');
}
}
Hope it turns out to be helpful.

Categories