Laravel 6 groupBy to belongsTo relationship - php

Hello I have Post model with relation:
public function category()
{
return $this->belongsTo(category::class);
}
I need show posts on every tab of category. For this I need use groupBy. When I do this:
$posts = Post::with('category')->groupBy('category.title')->get();
I get error:
Column not found: 1054 Unknown column 'category.title'.
Why? How I can return my posts with key of category title?
For multilangual I use this package: https://github.com/spatie/laravel-translatable

Try Collection's group-by method:
$posts = Post::with('category')->get()->groupBy('category.title')->all();
You may pass a callback to return the value you wish to key the group by (as you mentioned you are using laravel-translatable package):
$posts = Post::with('category')->get()->groupBy(function ($post, $key) {
return $post->category->getTranslation('title', 'fr');
})->all();

You can use something like this:
$posts = Post::all()->groupBy('category_id')->get();
and then in blade files you can foreach tabs and find a name by category_id

Related

Laravel 8: Filter/show data on blade view with array saved on db

I have a table called "products" which contains some data and a column called "category" with random category name. I have tried to query it on my blade view and the data are shown perfectly. Now I have another table called "categories" which contains arrays of specified categories example: ["Book", "Shirt", etc..]
ProductContoller.php
public function index($subdomain) {
//Table which has products data
$products = DB::table('products')->get();
//Table which has array saved data
$categories = DB::table('categories')->get();
return view('products.index', compact('products', 'categories'));
}
Now what I want to do is to filter products which match the categories specified on "categories" table and hide all the other products which doesn't match.
Please help, any suggestion will be gladly appreciated.
I'd go with something like this:
$categories = ['Book', 'Shirt'];
Product::query()->whereHas('category', function (Builder $query) use ($categories): void {
$query->whereIn('name', $categories);
})->get();
Using Eloquent's whereHas function
Or:
Product::all()->filter(function (Product $product) use ($categories): bool {
return in_array($product->category->name, $categories);
});
Using Collection's filter function
Assuming you're using models and relations.

Laravel return relationship data based on locale

I have posts table that has related table where I store different translations based on post_id now when I want to return translation data based on user selected locale it says:
mb_strpos(): Argument #1 ($haystack) must be of type string, Closure given
Here is my function
$posts = Post::with('translations', function($q) {
$q->where('translate_code', app()->getLocale());
})->get();
dd($posts); // returning error above
But if I do this
$posts = Post::with('translations')->get();
dd($posts);
I will get following results
Here is translations data details:
My question is:
How can I return the one translation that has current locale name only?
If you using callback in with then use array of relations like below. Older version of laravel was working which you mentioned but latest version need array of relations when using callback.
$posts = Post::with(['translations'=> function($q) {
$q->where('translate_code', app()->getLocale());
}])->get();
using conditional eager loading should be like this:
$posts = Post::with(['translations'=> function($q) {
$q->where('translate_code', app()->getLocale());
}])->get();
while the relation name is the key, a closer to get data should be the value for the associative array for 'with' statement.
You can query your relation by using whereHas().
Docs: Laravel Docs Relationships
For example:
$posts = Post::whereHas('translations', function ($query) {
$query->where('translate_code', 'en');
})->get();
Maybe try to put the app()->getLocale() into a variable first. (after trying this out in tinker, this does seem te return a string, so may passing it by reference might help).

Laravel: orderBy a column with collections

I need to OrderBy a column with collection.
I need to orderBy(updated_at, 'desc') all posts which owned by current logged user.
Here is my code :
$posts = auth()->user()->posts->sortByDesc('updated_at');
Here is User model :
class User extends Authenticatable
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
It doesn't return any errors also doesn't sort !
Any helps would be great appreciated.
P.S:
I know I can achieve this with :
$posts = Post::where('user_id', auth()->user()->id)->orderBy('updated_at', 'desc')->get();
But I would like to do the same thing with collections.
So this is how you sort with SQL:
$posts = auth()->user()->posts()->orderBy('updated_at', 'DESC');
And with collections:
$posts = auth()->user()->posts->sortByDesc('updated_at');
I've tested the 2nd one and it works as intended for me.
Documentation: https://laravel.com/docs/6.x/collections#method-sortbydesc
*Its available since Laravel 5.1
#devk is right. What I wrote in the first post is correct.
The problem was in DataTables in the the view.
It needed to add this line to the Datatables options:
"order": [[ 5, 'desc' ]], // 5 is the `updated_at` column (the sixth column in my case)
So this is working fine :
$posts = auth()->user()->posts->sortByDesc('updated_at');
Try adding the following to your User model
public function posts_sortedByDesc(){
return $this->hasMany(Post::class)->sortByDesc('updated_at');
}
Then get the posts by calling posts_sortedByDesc instead of posts

How to order by pivot table data in Laravel's Eloquent ORM

In my Database, I have:
tops Table
posts Table
tops_has_posts Table.
When I retrieve a top on my tops table I also retrieve the posts in relation with the top.
But what if I want to retrieve these posts in a certain order ?
So I add a range field in my pivot table tops_has_posts and I my trying to order by the result using Eloquent but it doesn't work.
I try this :
$top->articles()->whereHas('articles', function($q) {
$q->orderBy('range', 'ASC');
})->get()->toArray();
And this :
$top->articles()->orderBy('range', 'ASC')->get()->toArray();
Both were desperate attempts.
Thank you in advance.
There are 2 ways - one with specifying the table.field, other using Eloquent alias pivot_field if you use withPivot('field'):
// if you use withPivot
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range');
}
// then: (with not whereHas)
$top = Top::with(['articles' => function ($q) {
$q->orderBy('pivot_range', 'asc');
}])->first(); // or get() or whatever
This will work, because Eloquent aliases all fields provided in withPivot as pivot_field_name.
Now, generic solution:
$top = Top::with(['articles' => function ($q) {
$q->orderBy('tops_has_posts.range', 'asc');
}])->first(); // or get() or whatever
// or:
$top = Top::first();
$articles = $top->articles()->orderBy('tops_has_posts.range', 'asc')->get();
This will order the related query.
Note: Don't make your life hard with naming things this way. posts are not necessarily articles, I would use either one or the other name, unless there is really need for this.
For Laravel 8.17.2+ you can use ::orderByPivot().
https://github.com/laravel/framework/releases/tag/v8.17.2
In Laravel 5.6+ (not sure about older versions) it's convenient to use this:
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range')->orderBy('tops_has_posts.range');
}
In this case, whenever you will call articles, they will be sorted automaticaly by range property.
In Laravel 5.4 I have the following relation that works fine in Set model which belongsToMany of Job model:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
The above relation returns all jobs that the specified Set has been joined ordered by the pivot table's (eqtype_jobs) field created_at DESC.
The SQL printout of $set->jobs()->paginate(20) Looks like the following:
select
`jobs`.*, `eqtype_jobs`.`set_id` as `pivot_set_id`,
`eqtype_jobs`.`job_id` as `pivot_job_id`,
`eqtype_jobs`.`created_at` as `pivot_created_at`,
`eqtype_jobs`.`updated_at` as `pivot_updated_at`,
`eqtype_jobs`.`id` as `pivot_id`
from `jobs`
inner join `eqtype_jobs` on `jobs`.`id` = `eqtype_jobs`.`job_id`
where `eqtype_jobs`.`set_id` = 56
order by `pivot_created_at` desc
limit 20
offset 0
in your blade try this:
$top->articles()->orderBy('pivot_range','asc')->get();
If you print out the SQL query of belongsToMany relationship, you will find that the column names of pivot tables are using the pivot_ prefix as a new alias.
For example, created_at, updated_at in pivot table have got pivot_created_at, pivot_updated_at aliases. So the orderBy method should use these aliases instead.
Here is an example of how you can do that.
class User {
...
public function posts(): BelongsToMany {
return $this->belongsToMany(
Post::class,
'post_user',
'user_id',
'post_id')
->withTimestamps()
->latest('pivot_created_at');
}
...
}
You can use orderBy instead of using latest method if you prefer. In the above example, post_user is pivot table, and you can see that the column name for ordering is now pivot_created_at or pivot_updated_at.
you can use this:
public function keywords() {
return $this->morphToMany(\App\Models\Keyword::class, "keywordable")->withPivot('order');
}
public function getKeywordOrderAttribute() {
return $this->keywords()->first()->pivot->order;
}
and append keyword attribiute to model after geting and use sortby
$courses->get()->append('keyword_order')->sortBy('keyword_order');

Ordering Related Models with Laravel/Eloquent

Is it possible to use an orderBy for an object's related models? That is, let's say I have a Blog Post model with a hasMany("Comments"); I can fetch a collection with
$posts = BlogPost::all();
And then run through each post, and display the comment's last edited date for each one
foreach($posts as $post)
{
foreach($post->comments as $comment)
{
echo $comment->edited_date,"\n";
}
}
Is there a way for me to set the order the comments are returned in?
This is the correct way:
BlogPost::with(['comments' => function ($q) {
$q->orderBy('whatever');
}])->get();
The returned object from the relationship is an Eloquent instance that supports the functions of the query builder, so you can call query builder methods on it.
foreach ($posts as $post) {
foreach ($post->comments()->orderBy('edited_date')->get() as $comment) {
echo $comment->edited_date,"\n";
}
}
Also, keep in mind when you foreach() all posts like this, that Laravel has to run a query to select the comments for the posts in each iteration, so eager loading the comments like you see in Jarek Tkaczyk's answer is recommended.
You can also create an independent function for the ordered comments like you see in this question.
public function comments() {
return $this->hasMany('Comment')->orderBy('comments.edited_date');
}
And then you can loop them like you did in your original code.
Create a JOIN and select just the column you want to order on:
$post = BlogPost::join('comments', function($j) {
$j->on('posts.id', '=', 'comments.post_id');
$j->select('comment_date_or_other_col');
})
->orderBy('comment_date_or_other_col', 'DESC')->first();
Yes:
$posts = BlogPost::with('comments')
->orderBy('comments_table_name.column_name')
->get();
And you can also set that in your relation:
public comments()
{
$this->hasMany("Comments")->orderBy('comments.column_name');
}

Categories