Laravel order by related table not working - laravel

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

Related

How can I fetch value from 3 tables using laravel relationship?

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

How can I get a post with all related ones?

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

Laravel. How to get relationships where foreign key is an array

I am trying to retrieve database rows with their relationships. However, the local key is an array. Let me explain using an example.
Lets say I have a table of countries and a table of pages. Each country can have many pages. Each page can belong to multiple countries. I do not have the flexibility to change this schema.
pages
+-------------+-----------+
| id | name | countries |
+-------------+-----------+
| 1 | Page 1 | 1 |
+-------------+-----------+
| 2 | Page 2 | 1,2,3 |
+-------------+-----------+
| 3 | Page 3 | 4,5,6 |
+-------------+-----------+
countries
+----+----------------+
| id | name |
+----+----------------+
| 1 | United States |
+----+----------------+
| 2 | United Kingdom |
+----+----------------+
| 3 | Germany |
+----+----------------+
| 4 | France |
+----+----------------+
| 5 | Hong Kong |
+----+----------------+
| 6 | Thailand |
+----+----------------+
| 7 | Belgium |
+----+----------------+
| 8 | Singapore |
+----+----------------+
My model and controller look something like:
country
public function pages()
{
return $this->hasMany(Page::class, 'id', 'countries');
}
MemberController.php
$countries = Country::with('pages')->get();
This is returning all countries, but only Page 1 contains any relationships.
Is there a way to retrieve relationships using a whereIn approach so all three countries will return appropriate pages?
Thanks in advance
Since Page can belong to many Countries, you need to create a pivot table called country_page and remove the countries column.
Then define two belongsToMany() relationships in both models:
public function pages()
{
return $this->belongsToMany(Page::class);
}
If you're not following Laravel naming conventions listed in my repo and you gave the pivot name a custom name, define it too:
public function pages()
{
return $this->belongsToMany(Page::class, 'custom_pivot_table');
}
Something like this ?
$datas = Pages::where( ##your conditions### )->get()->inArray();
$countries = Countries::pluck('name','id'); // return array of id=>name
foreach($datas as $key=>$data) {
$c = [];
foreach(explode(',',$data['countries']) as $country_id) {
$c[]=$countries[$country_id];
//or
$c[]= ['id'=>$country_id,'name'=>$countries[$country_id]];
}
$datas[$key]['countries']= $c;
}

Laravel 5 - Eloquent JOIN confusion

I have two simple tables, cats and breeds:
mysql> select * from cats;
+----+--------+---------------+----------+---------------------+---------------------+
| id | name | date_of_birth | breed_id | created_at | updated_at |
+----+--------+---------------+----------+---------------------+---------------------+
| 1 | Rita | 2008-07-06 | 1 | 2015-05-09 20:40:49 | 2015-05-09 20:50:20 |
| 2 | Muni | 1992-05-15 | 3 | 2015-05-09 20:50:54 | 2015-05-09 20:50:54 |
| 3 | Hector | 2005-01-23 | 4 | 2015-05-09 21:08:23 | 2015-05-09 21:08:23 |
+----+--------+---------------+----------+---------------------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from breeds;
+----+------------+
| id | name |
+----+------------+
| 1 | Domestic |
| 2 | Persian |
| 3 | Siamese |
| 4 | Abyssinian |
+----+------------+
4 rows in set (0.00 sec)
All I want to do is get a list of the cats that are of a certain breed. In the cats table, breed_id is a FK that points to the PK of the breeds table. The example code in my book which is supposed to return a view with all cats of a specific breed returns no results:
The url I am trying is /cats/breeds/Domestic
The view that is generated uses this logic in the routes.php file:
Route::get('cats/breeds/{name}', function($name)
{
$cats = Furbook\Cat::with('breeds')
->whereName($name)
->get();
dd($cats);
});
When I dd($cats) I get zero results even though I have a cat with a domestic breed_id of 1. What have I done wrong here?
EDIT:
I can get it to work with the following hacky code, which first gets the id from the breeds table by querying against the name field, then queries the cats table with that id:
$breed = Furbook\Breed::whereName($name)->get();
$id = $breed[0]['attributes']['id'];
$cats = Furbook\Cat::whereId($id)->get();
How do I make this into one Eloquent query? I see no examples for this kind of query on the Laravel site.
For more information this is how the models look:
class Breed extends Model {
public $timestamps = false;
public function cats()
{
return $this->hasMany('Furbook\Breed');
}
}
class Cat extends Model {
protected $fillable = [
'name',
'date_of_birth',
'breed_id',
];
public function breed()
{
return $this->belongsTo('Furbook\Breed');
}
}
You're asking the system to give you all cats with a name of Domestic - not all cats of the breed name Domestic.
Assuming your model relationships are in order, you could do e.g.
$cats = Furbook\Breed::whereName($name)->first()->cats;
Also, my Burmese cat just hit the monitor; I think she's upset that she's not in the breeds list.

filter by first foreign key

I am creating a website to compare product prices in different stores. I have set up a database with these two tables.
+---------+ +------------+
| Product | | Price |
+---------+ +------------+
| id | | id |
| name | | product_id |
+---------+ | price |
| date |
+------------+
Now I would like to be able to get all the products where the last price.date is earlier than a certain date.
I have tried using the following code, but without success.
Product::with(['prices' => function($q){
$q->first()->where('date', '<', Carbon::yesterday());
}])->get()->prices();
Can someone help me with this problem?
Thanks in advance :)
With() is only for eager loading records.
You need to use whereHas:
Product::whereHas('price',function($q) {
return $q->where('date', '<', Carbon::yesterday());
})->whereHas('price',function($q) {
return $q->where('date','>=',Carbon::yesterday());
},'=',0)->get();
This will return all products where it has prices whose date is less than Carbon::yesterday()
Official Documentation

Resources