I have table order related to warehouse with related to warehouse_delivery.
With following structure:
+--------------------------------------+ +-----------+
| Order | | Warehouse |
+----+--------------+------+-----------+ +----+------+
| id | warehouse_id | cost | send_date | | id | name |
+----+--------------+------+-----------+ +----+------+
+--------------------------------------------------------------------------+
| WarehouseDelivery |
+----+--------------+------------------------+-----------------------------+
| id | warehouse_id | domestic_delivery_time | international_delivery_time |
+----+--------------+------------------------+-----------------------------+
Delivery time is stored as days.
And now I would like to search by send_date to find Orders where send_date + domestic_delivery_time is today.
I don't have a idea how can I grab that domestic_delivery_time added to send_date.
Order::where('warehouse_id', $warehouseId)
->with(['product', 'warehouse.warehouseDelivery'])
->get();
How to write query?
Thank you.
I'm not sure there's a database-agnostic solution for this.
You'd need to use DB::raw. Here's a solution using a scope that covers MySQL, SQL Server and PostgreSQL:
// On Owner.php
public function scopeDueToday($query) {
$today = \Carbon\Carbon::now()->format('Y-m-d');
$driver = $query->getConnection()->getDriverName();
return $query->whereHas('warehouse.warehouseDelivery', function ($deliveries) use ($today, $driver) {
$raw = [
'mysql' => "CAST(start_date + INTERVAL domestic_delivery_time days AS date)",
'pgsql' => "(start_date + (domestic_delivery_time || ' day')::interval)::date",
'sqlsrv' => "CONVERT(date, DATEADD(day, domestic_delivery_time, start_date))",
];
return $deliveries->where(\DB::raw($raw[$driver]), '=', $today);
});
}
// On your controller
Order::dueToday()->where('warehouse_id', $warehouseId)
->with(['product', 'warehouse.warehouseDelivery'])->get();
Related
I have 4 tables named: categories, products, blogs, companies.
+-----------+
| Category |
+----+------+
| id | name |
+----+------+
| 1 | Cat1 |
| 2 | Cat2 |
+----+------+
+-----------+
| Company |
+----+------+
| id | name |
+----+------+
| | |
+----+------+
+-------------------------+
| Product |
+----+-------------+------+
| id | category_id | name |
+----+-------------+------+
| 1 | 1 | P1 |
| 2 | 2 | P2 |
| 3 | 1 | P3 |
+----+-------------+------+
+---------------------------+
| Blog |
+----+------------+---------+
| id | product_id | heading |
+----+------------+---------+
| 1 | 1 | H1 |
| 2 | 2 | H3 |
| 3 | 3 | H4 |
+----+------------+---------+
Blog Model
public function product()
{
return $this->belongsTo(Product::class);
}
Product Model
public function company()
{
return $this->belongsTo(Company::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
Blog::with('product.category')
->where('status', 'Y')
->where('featured_position', 'Y')
->orderBy('id', 'DESC')
->get();
From the above tables the result will show 2 blogs namely blogs having id 1 and 3. But the above code is fetching result for all the blogs from the blog table.
You'll want to use a whereHas to query the relationship.
$categoryId = 1;
$productQuery = function ($query) use ($categoryId) {
// This $query object will be for the Product models, so we can treat it as
// such.
// We can query like we would on a Product, like Product::where([...]).
$query->with('category')->where('category_id', $categoryId);
};
$blogs = Blog::whereHas('product', $productQuery)
->with(['product' => $productQuery])
->get();
I've set the category ID to a variable, in case you need to change it during runtime.
Also, note that the with is completely optional.
If you exclude it, your query will run exactly the same, just without constrained eager loading. The effects of this are just that you will have to perform more database requests. The benefits come if you never actually need the relationship, then it won't have been fetch unnecessarily.
If you're curious what the SQL command will be, it will be:
SELECT * FROM `blogs`
WHERE EXISTS (
SELECT * FROM `products`
WHERE `blogs`.`product_id` = `products`.`id` AND `category_id` = ?
)
In simple terms, it will select everything from the blogs table.
It's then going to query the products table, using an inner join, to select products that have a corresponding blog entry.
The second part of the where clause is going to just get the specific character. The ? is because category_id can be any integer.
Catory with id 1
fetch its products
fetch blogs for each of its products ( map over products )
flatten the results ( since its gonna be nested for each product )
Category::find(1)->products->map->blogs->flatten();
you can use Tinker to interact with you application's query builder and eloquent models from terminal you can use :
$ php artisan tinker
For more clause you can use collection methods :
Category::find(1)->products->map->blogs->flatten()->where('status', 'Y')
->where('featured_position', 'Y')
->sortDesc('id') ;
I have two related tables:
projects:
| id | name |
| 1 | Test |
| 2 | Example |
project_dates:
| id | project_id | date |
| 1 | 1 | 2020-02-01 |
| 2 | 1 | 2020-02-10 |
| 3 | 1 | 2020-01-25 |
| 4 | 2 | 2020-01-10 |
| 5 | 2 | 2019-12-15 |
Now for example I would like to get all projects where the first date of project_dates is equal to 2020-01-25. So I tried the following:
$projects = Project::whereHas('dates', function($query) {
$query->whereRaw("MIN(date) = '2020-01-25'");
});
But this returns the following error:
SQLSTATE[HY000]: General error: 1111 Invalid use of group function
which revers to MIN(date). So my question remains how can I get all projects with the first date being equal to 2020-01-25 or any other specific date. From the example above I would expect to get the project with id 1 since the first date of 2 is 2019-12-15
I know that is possible to use a subqueryjoin however I feel like doing 2 queries shouldn't be required.
$firstDates = ProjectDate::groupBy('project_id')
->selectRaw('project_id, MIN(date) as first_date');
Project::joinSub($firstDates, 'first_dates', function($join){
$join->on('first_dates.project_id', '=', 'project.id')
->where('first_date', '=','2020-01-25');
})->get();
You cannot use where aggregate function with groupBy, try to use havingRaw instead:
$projects = Project::whereHas('dates', function($query) {
$query->havingRaw("MIN(date) = ?", ['2020-01-25']);
});
// Or
$projects = Project::whereHas('dates', function($query) {
$query->having(DB::raw("MIN(date)"), '2020-01-25');
});
$projects = Project::whereHas('dates', function($query) {
$query->having(DB::raw("MIN(date)"), '2020-01-25')
->groupBy( 'project_dates.id' ); // For sql_mode=only_full_group_by
});
Here is my table structures:
// tickets
+----+------------+----------------------+--------+---------+
| id | subject | content | closed | user_id |
+----+------------+----------------------+--------+---------+
| 1 | subject1 | question1 | 0 | 123 |
+----+------------+----------------------+--------+---------+
// answers
+----+----------------------+---------+-----------+
| id | content | user_id | ticket_id |
+----+----------------------+---------+-----------+
| 1 | answer1 | 123 | 1 |
| 2 | answer2 | 456 | 1 |
+----+----------------------+---------+-----------+
Now I need to get a ticket with along all its answers. I can do that using pure SQL like this:
SELECT t.*, a.*
FROM tickets t
LEFT JOIN answers a
ON t.id = a.ticket_id
WHERE t.id = ?
Now I'm using Laravel and I want to do that using with() class (however not sure it is possible). Any idea what change should I make in the models for doing that?
Noted that I can get just a ticket in Laravel like this:
$ticket = Tickets::where('id', $request->id)->get();
First make a hasMany() relationship in your Tickets model class.
public function answers()
{
return $this->hasMany(Answers::class, 'ticket_id', 'id');
}
And then in your controller query
$tickets = Tickets::with('answers')->where('id', $request->id)->get();
Let's say I have 3 tables, Events, Matches and Training. Events is a polymorphic table, it can be either Match or Training.
------------------ ----------- --------------
| Events | | Matches | | Trainings |
------------------ ---------- --------------
| id | | id | | id |
| name | | home | | type |
| eventable_id | | away | --------------
| eventable_type | -----------
------------------
Now, I create an event like this:
$match = Match::create(['home' => Team 1', 'away' => 'Team 2']);
$match->events()->create(['name' => 'Event name']);
Therefore I have this record in my Events table:
--------------------------------------
| 1 | 'Event name' | 1 | 'App\Match' |
--------------------------------------
And in Matches:
---------------------------
| 1 | 'Team 1' | 'Team 2' |
---------------------------
My Event Model:
public function eventable() {
return $this->morphTo();
}
My Match/Training Model:
public function events() {
return $this->morphMany('App\Event', 'eventable');
}
However, what if I want to update that Event and I want to change it from Match To Training but keep the event.
Is there a more Eloquent way or do I have to do it like this ?
$event->eventable->delete();
$training = Training::create(['type' => 'Stamina Training']);
$event->update([
'name' => $request->name,
'eventable_id' => $training->id,
'eventable_type' => 'App\Training'
]);
Alright, I also faced the same issue and the solution is to use the associate function on the relation. Instead of deleting the "eventable" on the "event", you can simply associate another model instance to the morph relationship. like below.
$training = Training::create(['type' => 'Stamina Training']);
$event->eventable()->associate($training);
$event->update([
'name' => $request->name
]);
I solved this issue by following the same convention on this related question. It's not a Laravel way but it could do the trick.
I have this structure.
class Product extends Model{
public function office()
{
return $this->belongsTo(Office::class,'office_id');
}
}
I want to list products order by office.name.
this is the query
$res = \App\Product::with(['office' => function($q){
$q->orderBy('offices.name','asc');
}])->get();
this is the output loop
foreach($res as $key => $val){
print "<br />user: ".$val->id.", office: ".$val->office->id;
}
this is the Product data:
+----+--------+
| id | name |
+----+--------+
| 1 | Life |
| 2 | Cars |
| 3 | Health |
| 4 | House |
+----+--------+
this is the data in Office
+----+----------------+
| id | name |
+----+----------------+
| 1 | First office |
| 2 | working office |
+----+----------------+
The order by is not affecting the result.
same result, the order by like not existed.
Thanks
In your code you are simply "ordering" the offices by name, which means if each product had many offices, it would sort the offices alphabetically.
To sort (OrderBY()) a collection, the column has to be an attribute of the collection object. One solution could be to Join your models. SOmething like this might help you.
$res = Product::with('office')
->join('offices', 'products.office_id', '=', 'offices.id')
->select('products.*', 'offices.name')
->orderBy('office.name')
->get();