Laravel/Eloquent ORM - retrieve only referenced records - php

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';

Related

Get results by finding an ID which is two joins away using Eloquent relationships

I need to get rows from one table using an id which is two joins away.
I know I can use join('table_name') but I am trying to use the model names rather than raw table names.
I'm trying to select shipping_shipment.* by joining order_item_join_shipping_shipment then joining order_item, and filtering where order_item.order_id = x
I tried this in the ShippingShipment class, but I can't figure it out.
return $this->hasManyThrough(OrderItem::class, ShippingShipment::class, 'shipment_id', 'order_item_id', 'id', 'id');
There are many items in an order, and many shipments. I need to get the shipments.
There can be more than one shipment per order - items come from various places.
There can be more than one shipment per item - if something is returned and needs shipping again.
The table I want to get rows from, shipping_shipment, is joined to order_item by a join table order_item_join_shipping_shipment. That join table has the order_item_id. I need then to join order_item table so that I can search for order_item.order_id
Table order_item model OrderItem
+-----+---------------+
| id | order_id |
+-----+---------------+
| 6 | 13464 |
| 8 | 13464 |
| 9 | 13464 |
+-----+---------------+
Table order_item_join_shipping_shipment model OrderItemJoinShippingShipment
+-----+---------------+-------------+
| id | order_item_id | shipment_id |
+-----+---------------+-------------+
| 1 | 6 | 12 |
| 1 | 9 | 12 | two items in one shipment
| | | |
| 2 | 8 | 13 |
| 3 | 8 | 14 | one item was returned so shipped again
+-----+---------------+-------------+
Table shipping_shipment don't need describing except to say it has an id column.
If I was to do it with MySQL it would look like this
SELECT ss.*, oiss.order_item_id FROM
order_item_join_shipping_shipment AS oiss
INNER JOIN shipping_shipment AS ss ON (oiss.shipment_id = ss.id)
INNER JOIN order_item AS oi ON (oiss.order_item_id = oi.id)
WHERE oi.order_id = 13464
I noticed you are not using the default table names, so your Models must have the table names explicit, e.g.:
class OrderItem extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'order_item';
}
In the same Model file of the above example, you need to indicate how the relationship works, i.e.:
public function shippingShipments()
{
return $this->belongsToMany(ShippingShipment::class, 'order_item_join_shipping_shipment', 'order_item_id', 'shipment_id');
}
Here you can check in Laravel documentation the whole explanation.
You need to apply the same concept in ShippingShipment Model, so your Model will be something like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'order_item';
/**
* The roles that belong to the user.
*/
public function orderItens()
{
return $this->belongsToMany(OrderItem::class, 'order_item_join_shipping_shipment', 'shipment_id', 'order_item_id');
}
}
This way you can get shipments by order item and vice-versa:
//Shipments of Order item 13464
$shipments = OrderItem::find(13464)->shippingShipments()->get();
//Order items of Shipment 1
$orders = ShippingShipment::find(1)->orderItems()->get();
Source: Laravel Documentation
As far as I can tell you are using a pivot table between ShippingShipment and OrderItem. If I understand you correctly you want to get OrderItems that are connected to ShippingShipment, if that is the case this is what you can do:
Make belongs to many relationships in both models, such as:
ShippingShipment:
public function orderItems(){
return $this->belongsToMany(OrderItem::class, 'table_name', 'column_id');
}
OrderItem:
public function shippingShipment(){
return $this->belongsToMany(ShippingShipment::class, 'table_name', 'column_id');
}
And then you can get the desired result by typing this query:
ShippingShipment::find(1)->with('orderItems')->get();
OrderItem::find(13464)->with('shippingShipments')->get();
Note: you can use orderItems:id,order or shippingShipment:id,some_other_field for more optimized query

Product with Multiple Product Related using Laravel Model HasMany() & BelongsTo?

As of now I have 2 different Model
ProductModel.php Which is my list of products.
ProductRelated.php Which is the assigned related product per specific product.
The scenario is,
If I have id = 1 in my ProductModel. id (primary key of Product Model)
and in my ProductRelated.php I have this
prodID (Which is the equivalent of id (primary key of Product Model)
This will serve to identify which specific product id the related products are being assiged.
prodRelatedID this is the products related to that product.
So my ProductModel will have this data
id | prodTitle | prodDesc | More fields here...
1 | Product 1 | Description | More FIeld value here ..
2 | Product 2 | Description | More FIeld value here ..
3 | Product 3 | Description | More FIeld value here ..
In my ProductRelated will have this data
id | prodID | prodRelatedID | More fields here...
1 | 1 | 2 | More FIeld value here ..
2 | 1 | 3 | More FIeld value here ..
Which means, id = 1 from ProductModel has 2 different prodRelatedID
and those are the 2 and 3
My idea by doing this is to use hasMany()
Here's my ProductModel
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\User;
class ProductModel extends Model
{
public $timestamps = true;
protected $table = 'product';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id',
'prodTitle',
'prodDesc',
'prodCategory',
'attachment',
'prodSize',
'prodPrice',
'created_by',
'is_best_seller',
'prod_included',
'prod_instruction',
];
}
My ProductRelated
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\User;
class ProductRelated extends Model
{
public $timestamps = true;
protected $table = 'product_related';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id',
'prodID',
'prodRelatedID',
'created_by'
];
}
If you just want to get all related products per specific product, I do not think you need ProductRelated.
In your ProductModel
public function relatedProducts()
{
return $this->belongsToMany(ProductModel::class, 'product_related', 'prodID', 'prodRelatedID');
}
And then you should be able to get all records like this:
$relatedProducts = ProductModel::find(1)->relatedProducts()->get();

Illegal offset type error on fetching a relations to self class in laravel

My User model is like this :
class User extends Authenticatable
{
protected $primaryKey = 'user_id';
protected $fillable = [
'username',
'password',
'name',
'family',
'supervisor'
];
public function children()
{
return $this->hasMany(\App\User::class, 'supervisor', 'user_id')->with('children');
}
}
As you can see there a supervisor column that specify parent of a user.
Now to fetch all children of user models that have supervisor= null, I wrote this :
return User::with('children')->whereNull('supervisor')->get();
but it return this error always :
PHP Warning: Illegal offset type in D:\wamp\www\zarsam-app\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Relations\HasOneOrMany.php on line 168
Table users have these data :
+---------+-------------+---------+--------------+------------+
| user_id | username | name | family | supervisor |
+---------+-------------+---------+--------------+------------+
| 1 | 09139616246 | ahmad | badpey | null |
| 7 | alinasiri | ali | nasiri arani | 1 |
| 8 | zahedi | mostafa | zahedi | 1 |
| 9 | hsan | hasan | ghanati | 8 |
+---------+-------------+---------+--------------+------------+
Update :
I found that problem is that I have a accessor same name supervisor attribute like this :
public function getSupervisorAttribute($value)
{
return is_null($value) ? null : User::select('user_id', 'name', 'family')->find($value);
}
I added that because I want to return supervisor user as an object.
But now in this case, what do I do ?
Try with this setup:
public function children()
{
return $this->hasMany(\App\User::class, 'supervisor', 'user_id');
}
And fetching them like
return User::with('children.children')->whereNull('supervisor')->get();
If this doesn't resolve your issue, then your relationships are not well defined, then I suggest reading about foreignand local keys inside your children.children model
NOTE:
HasOneOrMany.php
...
return $results->mapToDictionary(function ($result) use ($foreign) { //line in question
...
And when I found that method, this is what it said:
/**
* Run a dictionary map over the items.
*
* The callback should return an associative array with a single key/value pair.
*
* #param callable $callback
* #return static
*/
public function mapToDictionary(callable $callback)
I want to point out The callback should return an associative array with a single key/value pair. Which means you cannot do it recursively, at least I believe you can't.

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