In my Post model there is an author relationship belonging to a User (related by the typical user_id field in the database table) to represent the author of the post and there is also a submitter relationship to represent a user that submitted a post for review. The submitter user could be the same as the author user or it could be a different user.
Here is the migration:
Schema::create('posts', function(Blueprint $table) {
$table->increments('id');
$table->string('code');
$table->integer('user_id');
$table->integer('submitter_id');
});
Here is the Post model:
class Post extends Model
{
protected $guarded = [];
public function author()
{
return $this->belongsTo(User::class);
}
public function submitter()
{
return $this->belongsTo(User::class);
}
}
When querying for a post by id I can eager load those relationships
$post = Post::with(['submitter', 'author'])->find($id);
If the seeder inserts two posts, one where the user_id and the submitter_id are the same, then one where they are different:
DB::table('posts')->insert([
'user_id' => 1,
'submitter_id' => 1
]);
DB::table('posts')->insert([
'user_id' => 1,
'submitter_id' => 2
]);
When querying for the second post I expect it to run two queries for the different users, since the user_id and submitter_id are different:
[2023-02-02 17:51:48] testing.INFO: sql: select * from "users" where "users"."id" in (2)
[2023-02-02 17:51:48] testing.INFO: bindings: array (
)
[2023-02-02 17:51:48] testing.INFO: query time: 0.2
[2023-02-02 17:51:48] testing.INFO: sql: select * from "users" where "users"."id" in (1)
[2023-02-02 17:51:48] testing.INFO: bindings: array (
)
[2023-02-02 17:51:48] testing.INFO: query time: 0.18
However for the first post, the user_id and the submitter_id are the same, yet a separate query is run to get each record:
[2023-02-02 17:51:48] testing.INFO: sql: select * from "users" where "users"."id" in (1)
[2023-02-02 17:51:48] testing.INFO: bindings: array (
)
[2023-02-02 17:51:48] testing.INFO: query time: 0.15
[2023-02-02 17:51:48] testing.INFO: sql: select * from "users" where "users"."id" in (1)
[2023-02-02 17:51:48] testing.INFO: bindings: array (
)
[2023-02-02 17:51:48] testing.INFO: query time: 0.13
While I understand that there is a possibility that the two queries could return different results (e.g. if a separate query was run in a separate process to update the user record), the chances of that are dramatically low.
Can the query (and its results) be used for both relations when the primary key is the same? If so, how can this be achieved?
It can't. Two different relationships is considered as two different objects by Laravel, even when it's referencing to the same table.
The doc also states that eager-load feature exists to "alleviates the "N + 1" query problem", not the problem that you asked about.
You can make your own query using the query-builder if you want to retrieve it in one query. But that will gives you a different returned data structure and adds a bit of complexity.
Related
I have 3 tables, Order, Products, Order_Products. I need get all field from order and products, thats ok using hasManyThrough(). But i need 1 more field from Order_products. How can i get this field ?
public function products()
{
//in order model
return $this->hasManyThrough('App\Models\Product','App\Models\OrderProduct','order_id','id','id','product_id');
}
using sql i need query like
SELECT
products.*, order_products.order_id, order_products.count as order_count
FROM
products
INNER JOIN order_products ON order_products.product_id = products.id
WHERE
order_products.order_id = 2
You can access intermediate table fields by using pivot attribute on model.
Lets say you have product, Then you can access count field of orders_products
$product->pivot->count;
I have a One To Many (Inverse) relation on my laravel 5.4 application. There are two models Sale and Vehicle which are related and associated with the scene.
The relation on the Sale model is :
public function vehicle()
{
return $this->belongsTo('App\Models\Vehicle','vehicle_id');
}
Table sales has following fields :
id, vehicle_id, date_time, status etc.
Table vehicles has following fields :
id, reg_number, volume, status etc.
What I want is, sum of the field 'vehicles.volume' of Sale records within the search conditions.
I have tried some methods like the following one:
$query = Sale::where('status', 1);
$query = $query->where('date_time', '<', "2017-05-10 10:10:05");
$totalVolume = $query->whereHas('vehicle')->sum('vehicles.volume');
and it resulted in the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'vehicles.volume' in 'field list' (SQL: select
sum(vehicles.volume) as aggregate from sales where status = 1
and date_time < "2017-05-10 10:10:05" and exists (select * from vehicles where sales.vehicle_id =
vehicles.id))
hopefully waiting for a solution to 'get the sum of the volume of the sales' using eloquent query.
Edited
You need to use a join before summing the vehicles.volume column
$totalVolume = Sale::where('status', 1)
->where('date_time', '<', "2017-05-10 10:10:05")
->join('vehicles', 'vehicles.id', '=', 'sales.vehicle_id')
->select(DB::raw('sum(vehicles.volume) as total_volume');
select sum(volume) as aggregate from vehicles INNER JOIN sales ON vehicles.id=sales.vehicle_id
I have the next question. How can I solve that problem? I have two tables vehicles and vehicles_extras. In the first table have a lot of columns. In the second table I have only 4 columns:
id, vehicle_id, vehicletype, vehicle_extra
The id is autoincrement, the vehicle_id is from the first table the id column, the 'vehicletype' is a string, type of the vehicle, the vehicle_extra is the number of the extras.
So I have a search form,where is the extras are checkboxes, extra[]. In a web.php I have this code:
$data1=DB::table('vehicles_extras')->where('vehicletype','$vt')->where('vehicle_extra',$extras);
....
if(Input::has('extras'))
$query->union($data1);
$data=$query->get();
for that I get error:
SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select count(*) as aggregate from vm_vehicles where vehicletype = car) union (select * from vm_vehicles_extras where vehicletype = car and vehicle_extra = 2))
But ok, let's say it's working, but I have array with more values, so I want to use whereIn .
Any idea? I use Laravel 5.3.
First, you have to create one-to-many relation between vehicles and vehicles_extras.
Add the following relation in your Vehicle model:
public function vehicleExtras()
{
return $this->hasMany('App\VehicleExtra');
}
Then you can query it as:
Vehicle::whereHas('vehicleExtras', function ($q) use($extra) {
$q->where('vehicletype', 'car')
->whereIn('vehicle_extra', $extra);
})->get();
My entity (Product) has a unidirectional many-many relation with another entity (Company) called deletedByCompanies.
I wish to select all Products that has not been deleted by a particular company. That is all products that are not connected through that many-many relation.
Tried:
$this->em->getRepository(Product::class)->createQueryBuilder('t')
->leftJoin('t.deletedByCompanies', 'deletedCompany')
->andWhere('deletedCompany.id not in (:companyId)')
->setParameter('companyId', [$companyId]);
But this simply does not return anything.
Schema is pretty straghtforward:
Product:
id: int PK
Company:
id: int PK
DeletedProducts
product_id: int FK
company_id: int FK
Entity definition in Product class:
/**
* #var Company[]
* #ORM\ManyToMany(targetEntity="Company", indexBy="id")
* #ORM\JoinTable(name="DeletedProducts")
*/
protected $deletedByCompanies;
I think you can solve your problem with a NOT EXISTS clause on the deletedby table.
In the SQL dialect:
SELECT * FROM product p WHERE NOT EXISTS
(SELECT * FROM DeletedProducts d WHERE p.id=d.product_id AND company_id = 2 );
In the Doctrine2 DQL, we haven't the entity DeletedProducts so we have to do a bit more stuff like:
$qb = $this->em->getRepository("AcmeDemoBundle:Product")->createQueryBuilder('t')
->Join('t.deletedByCompanies', 'deletedCompany')
->andWhere('deletedCompany.id in (:companyId)')
->andWhere("p=t");
$mainQb = $this->em->getRepository("AcmeDemoBundle:Product")->createQueryBuilder('p');
$mainQb->where($mainQb->expr()->not($mainQb->expr()->exists($qb->getDQL())));
$mainQb->setParameter('companyId', [$companyId]);
var_dump($mainQb->getQuery()->getSql());
$res =$mainQb->getQuery()->execute();
Let me know if I don't understand your problem.
Hope this help
I'm not familiar with Doctrine, but I'm trying to help with some SQL knowledge. Following query should do what you want:
SELECT DISTINCT Product.* FROM Product
LEFT JOIN DeletedProducts on product_id = product.id
WHERE product_id IS NULL OR product_id !=
ALL( SELECT product_id FROM DeletedProducts WHERE company_id = 2 )
Some Explanation...
DISTINCT: Nessacary keyword to prevent redundancy. The same product can appear multiple times in the result of the Left Join if it is deleted by . DISTINCT eliminates those duplicates.
WHERE product_id IS NULL: The Left Join will also list the products which are not related to any company over the "DeletedProducts"-table. Since there is no relation the fields product_id and company_id are NULL.
OR product_id != ALL( [...] ): Now that we have got the products which aren't deleted by any company, we need additionally those which aren't deleted by a particular company. So we use an OR with a subquery which selects all deleted products of a certain company (e.g. company id = 2 as in the code example). Since we want to have the not-deleted products we have to use the "!="-Operator.
I hope this helps a little. Now it's up to you to "translate" the query for the usage within Doctrine.
I have 2 databases (AccessControls & User).
AccessControl table has this fields: AC_id, User_id, AccessControls.
User table has this fields: User_id, User_name, UserType.
What I wanted to achieve is:
- Printing out users that has a UserType = 1 (in user table) and User_id which does not exist in AccessControl table.
The results will be stored in a table in my view.blade.php
In the meantime, I managed to get the 1st part, which is users with UserType = 1. However, I have no idea on how I could get the 2nd part, which is to check if the User_id exists in AccessControl table.
Inside my controller:
$user = UserEntry::where('UserType', '=', 1)->get();
I have a rough idea that the query should be something like:
Select statement Where user.User_id != AccessControl.User_id
Any ideas on how I could achieve both parts in a single query?
Either not exists
User::where('UserType',1)
->whereNotExists(function ($q) {
$q->from('AccessControl')
->where('AccessControl.User_id', DB::raw('users.id'));
})
->get();
or leftJoin:
User::where('UserType',1)
->leftJoin('AccessControl','AccessControll.User_id','=','users.id')
->whereNull('AccessControll.id')
->get(['users.*']);
Check performance, suppose left join will be faster.
The SQL way would be a LEFT JOIN in combination with ISNULL():
SELECT
*
FROM
users
LEFT JOIN
AccessControl
ON
user.User_id = AccessControl.User_id
WHERE
users.userType = 1
AND
AccessControl.User_id IS NULL
I'm sure you get this translated to laravel.