Laravel 6.x Advanced String search locally [closed] - php

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have Laravel 6.x and need an advanced search for mariaDB. I would need a search that searches all columns if they contain a specific string.
Example:
+----+-------+--------------+
| id | name | lastname |
+----+-------+--------------+
| 1 | peter | peterjackson |
+----+-------+--------------+
| 2 | petery| hans |
+----+-------+--------------+
| 3 | hans | han |
+----+-------+--------------+
| 4 | petty | bun |
+----+-------+--------------+
Search querys:
peter: 1
peters: /
pet: 1,2,3
I tried already TNT-search but it only searches if the whole string is the same. So pet would only trigger at id=2.
Example for TNT-Search (Laravel Scout):
People::search("pet")->get()
*no records*
People::search("peter")->get()
record id 1 (id 2 not included)
Algolia search isn't an option because I can't outsource data into other data-centers.

This is untested and could certainly be improved, but this should do what you want:
Add the below methods to your model, or better yet your base model which is extended by all your other models:
/**
* An array containing the names of all of this model's columns
* #var []
*/
private $_columnNames = [];
/**
* Get an array of info for the columns of the given connection and table
* #return array
*/
public function columnInfo()
{
return DB::connection($this->connection)->select(DB::raw('SHOW FULL COLUMNS FROM '.$this->table.';'));
}
/**
* Get an array of all the column names for this db model
* #return array
*/
public function getColumnNames()
{
if (!$this->_columnNames) {
$this->_columnNames = Arr::pluck($this->columnInfo(), 'Field');
}
return $this->_columnNames;
}
/**
* Get all records where any table column is like the given value
* #param string $value
* #param array $selectColumns An array of columns to return
* #return \Illuminate\Database\Query\Builder
*/
public static function whereAnyColumnLike($value, $selectColumns)
{
$queryColumns = (new self)->getColumnNames();
$selectColumns = $selectColumns ?: $queryColumns;
$query = self::select($selectColumns);
foreach($queryColumns as $key => $column) {
$function = $key === 0 ? 'where' : 'orWhere';
$query->$function($column, 'LIKE', $value);
}
return $query;
}
Then you can call SomeModel::whereAnyColumnLike('%pet%')->get();

Related

Doctrine Distinct Count [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
What is the best practice for retrieving the DISTINCT COUNT on an entity collection?
In this example entity (Customer), I have a oneToMany relationship with Orders.
I want to count how many sales & products the customer has ordered:
> select * from orders;
+----------+----------+----------+
| customer | sale_ref | prod_ref |
+----------+----------+----------+
| 1 | sale_1 | prod_1 |
| 1 | sale_1 | prod_2 |
| 1 | sale_2 | prod_1 |
| 1 | sale_3 | prod_3 |
+----------+----------+----------+
> select count(prod_ref) from order where customer = 1;
+-----------------+
| count(prod_ref) |
+-----------------+
| 4 |
+-----------------+
> select count(distinct(sale_ref)) from order where customer = 1;
+-----------------+
| count(prod_ref) |
+-----------------+
| 3 |
+-----------------+
Here is the code
use Doctrine\ORM\Mapping as ORM;
class Customer
{
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\OneToMany(targetEntity="Orders", mappedBy="customer", cascade={"persist", "remove"}, fetch="EXTRA_LAZY")
*/
protected $orders;
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getOrders(): \Doctrine\Common\Collections\Collection
{
return $this->orders;
}
/**
* #return int
*/
public function getOrdersProductCount(): int
{
return $this->orders->count();
}
}
class Orders
{
/**
* #var Customer $customer
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="orders")
*/
protected $customer;
/**
* Non-unique sales reference
* #var string $salesRef
* #ORM\Column(name="sales_ref", type="string")
*/
protected $salesRef;
/**
* Unique product reference
* #var string $productRef
* #ORM\Column(name="product_ref", type="string")
*/
protected $productRef;
/**
* #return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
/**
* #return string
*/
public function getProductRef(): string
{
return $this->productRef;
}
/**
* #return string
*/
public function getSalesRef(): string
{
return $this->salesRef;
}
}
Using the Customer->getOrdersProductCount() works perfectly fine for retrieving the product count and is said to be "good practice" as it doesn't hit the database with full loading of the collection:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/extra-lazy-associations.html
If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection Collection#count()
However, in this example, a Customer can have multiple products for a sale - where the salesRef is non-unique. What is the best method for retrieving a DISTINCT COUNT of the salesRef?
This could/should be handled in the entity repository class:
class OrdersRepository
{
public function getSalesCount($customer): int
{
return (int)$this->createQueryBuilder('o')
->select('COUNT(DISTINCT(o.salesRef))')
->where('o.customer = :customer')
->setParameter('customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
}
public function getProductCount($customer): int
{
return (int)$this->createQueryBuilder('o')
->select('COUNT(o.productRef)')
->where('o.customer = :customer')
->setParameter('customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
}
}
This works BUT I need to load the entityManager/CustomerRepository in order to access these methods - whereas at least I can retrieve the product count from within the entity....
How could I access the distinct sales count from within the Customer entity - if at all?
I have considered using the Collection#filter() method and/or looping through the Orders entity to create an array with the salesRef as the key and then using array_unique#count() but this doesn't seem "right" - I suspect I know the answer (use the entity repository) but I would prefer to be able to access the sales count from within the Customer entity - what is the best practice/method?
I think this should do it and would be a more portable way of doing it. I did not test it though.
$qb = $this->createQueryBuilder('o');
return (int)$qb
->select($qb->expr()->countDistinct('o.salesRef'))
->where('o.customer = :customer')
->setParameter('o.customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
Reference is here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/query-builder.html#the-querybuilder
Hope this helps

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);
}]);
}

Laravel: Get users based on Eloquent relation

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.

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,
));

Categories