How do i join two queries and then paginate - laravel-5

$data = Category::join('articles', function ($join) {
$join->on('categories.id', 'articles.category_id')
->where('categories.type', 'news')
->where('articles.status', '1');
})->get()->sortByDesc('updated_at')->paginate(5);
I have 2 variables both querying the same model. I want to now merge these 2 separate datasets into a variable called $articles and then paginate on it.

Remove the get() method from the code like:
$data = Category::join('articles', function ($join) {
$join->on('categories.id', 'articles.category_id')
->where('categories.type', 'news')
->where('articles.status', '1');
})->sortByDesc('updated_at')->paginate(5);
It should work.

When joining queries and paginate in controller, to view the output in paginate you should include this under you for loop output:
{{ $data->links() }}

Related

Laravel, eloquent, query: problem with retriving right data from DB

I'm trying to get the right data from the database, I'm retriving a model with a media relation via eloquent, but I want to return a photo that contains the 'main' tag stored in JSON, if this tag is missing, then I would like to return the first photo assigned to this model.
how i assign tags to media
I had 3 ideas:
Use orWhere() method, but i want more likely 'xor' than 'or'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main')->orWhere();
}]);
return $models->paginate(self::PER_PAGE);
Raw SQL, but i don't really know how to do this i tried something with JSON_EXTRACT and IF/ELSE statement, but it was to hard for me and it was a disaster
Last idea was to make 2 queries and just add media from second query if there is no tag 'main'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}]);
$models_all_media = Model:: with(['media']);
return $models->paginate(self::PER_PAGE);
but i tried something like
for($i=0; $i<count($models); $i++) {
$models->media = $models_all_media
}
but i can't do this without get() method, beacuse i don't know how to change this to LengthAwarePaginator class after using get()
try using whereHas https://laravel.com/docs/9.x/eloquent-relationships
Model::with('media')
->whereHas('media',fn($media)=>$media->whereJsonContains('custom_properties->tags', 'main'))
->paginate(self::PER_PAGE);
as per your comment you can use
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}])
->leftJoin('media', function ($join) {
$join->on('models.id', '=', 'media.model_id')
->whereNull('media.custom_properties->tags->main');
})
->groupBy('models.id')
->paginate(self::PER_PAGE);
return $models;

Laravel Foreach sum result with while filter

Here my table
:chart_of_account_category
id
name
:chart_of_accounts
id
chart_of_accounts_category_id
:ledger_book
id
transaction_id
chart_of_accounts_id
:transaction
id
name
date
total
this is what i want to achieve
Controller
$chart_of_accounts_category = ChartOfAccountsCategory::get();
return view ('backend.ledger_book.index', compact('chart_of_accounts_category'));
Blade/View
#foreach ($chart_of_accounts_category as $account_category )
#foreach ($account_category->ChartOfAccounts as $item)
$item->LedgerBook->where('chart_of_accounts_id', $item->id)->sum('transaction.total'), 2)
#endforeach
#endforeach
i can get the sum thru this, but i still need to filter few things more, which i done it in blade. but cant get the filter works properly especially where like with symbol %, i would minimize query inside blade, is there anywhere i can get the result thru controller?
$item->LedgerBook->where('chart_of_accounts_id', $item->id)->where('transaction.date', 'like', '2019-06-%')->sum('transaction.total'), 2)
Instead of 'like' operator, you can use
->whereYear('transaction.date', '2019')->whereMonth('transaction.date', '06').
Also, I would suggest you to eager load ChartOfAccounts, LedgerBook and Transaction in your Controller. This will make your foreach loops much faster.
$chart_of_accounts_category = ChartOfAccountsCategory::with(['chartOfAccounts' => function ($query) {
$query->with(['ledgerBooks' => function ($query) {
$query->with(‘transaction’)
->whereHas(‘transaction', function ($query) {
$query->whereYear('date', '2019')
->whereMonth('date', '06');
});
}]);
}])
->get();

Laravel pluck an array from nested relationship

I need to get only the roomnumber arrays returned from the following query:
$roomnumbers = Room::with(['floorroomcount' => function($query){
$query->with('roomnumber')->get();
}])->where('roomtype_id', $roomtype_id)->get();
Tried:
The follow pluck is returning floorroomcount
$roomnumbers->pluck('floorroomcount');
but i need roomnumber array, how can i get?
This gives you all roomnumber results in one collection:
$roomnumbers->pluck('floorroomcount')->collapse()->pluck('roomnumber')->collapse();
You may shorten #Jonas Staudenmeir's answer like so:
$roomnumbers->pluck('floorroomcount.*.roomnumber.*')->collapse();
pluck('*') is essentially the same as collapse() in this particular context.
This is working, but with many loop and echoing directly, if anything can be simplified please let me know :
$roomnumbers = Room::with(['floorroomcount.roomnumber'])->where('roomtype_id', $roomtype_id)->get();
$floorroomcounts = $roomnumbers->pluck('floorroomcount');
$records = $floorroomcounts->map(function($floorroomcount, $value){
return $floorroomcount->pluck('roomnumber')->flatten();
})->values()->all();
foreach($records as $record){
foreach($record as $row){
echo '<option value='.$row->id.'>'.$row->roomnumber.'</option>';
}
}
//return response()->json($roomnumbers);
Try,
$roomnumbers = Room::with(['floorroomcount' => function($query){
$query->with('roomnumber')->get();
}])
->where('roomtype_id', $roomtype_id)
->get();
$records = $roomnumbers->map(function($element, $value){
return $element->map(function($e, $v){
return $e->roomnumber;
});
})->values()->all();
map() is a Laravel collection method so you need to import the collection facade on the top of the controller like: use Illuminate\Support\Collection;
In Laravel 5.1 and + you can use flatten() on collection.
method flattens a multi-dimensional collection into a single dimension:
$roomnumbers->flatten()->pluck('floorroomcount');

Order by sum column relationship in Laravel

I have this controller which grabs posts from a post table.
Every post in the posts table have the relation "hasMany" with another table likes.
Controller:
public function getDashboard(){
$posts = Post::orderBy('created_at', 'desc')->get();
return view('dashboard', ['posts' => $posts]);
}
I'd like to replace 'created at' with something like:
$post->likes->sum(like)
Don't know how to write the right syntax though.
EDIT:
Here are the tables.
Posts
--id
--body
Likes
--id
--post_id
--like
The column like can have the value 1 or -1.
I'd like to order on the summation of that column for each post.
So a post with one dislike(-1) and one like(1) will have the aggregated value of 0, hence will be placed after a post with one like(1).
You can use withCount() for this as:
Post::withCount('likes')->orderBy('likes_count')->get()
withCount() will place a {relation}_count column on your resulting
models
Update
Post::withCount(['likes' => function($q) {
$q->where('like', 1)
}])
->orderBy('likes_count')
->get()
Update2
You can use sortByDesc() to sort your collection as:
$posts = Post::get();
$posts = $posts->sortByDesc(function ($post) {
return $post->likes->sum('like');
});
If you want to sum column value from relationship and then sort records.
$users = User::addSelect(['likes' => Post::selectRaw('sum(likes) as total_likes')
->whereColumn('user_id', 'useres.id')
->groupBy('user_id')
])
->orderBy('likes', 'DESC')
->get()
->toArray();
Or
use below
$users = User::select("*",
\DB::raw('(SELECT SUM(likes) FROM likes_table WHERE likes_table.user_id = users.id) as tolal_likes'))
->orderBy('likes', 'DESC')
->get()
->toArray();

Laravel conditional on relation: return first()

I have a Laravel 5.2 one-to-many relation and I want to return the model and put a condition to relation.
I've tried this:
$categories = Category::with(['descriptions' => function($d) use ($default_language) {
$d->where('language_id', $default_language->id);
}])->get();
It work fine, I just want something else: the relation should not be a collection or array, just a simple object. I want to do something like
$d->where('language_id', $default_language->id)->first();
, just in this case first() is not working. Any ideas?
EDIT
Actually first() is not working properly, it returns first description just for the first object returned, for others it return nothing.
Try this:
$categories = \Jobinja\CMS\Blog\Models\Category::with([
'descriptions' => function ($q) use ($defaultLanguage) {
return $q->where('language_id', $defaultLanguage->id)->take(1);
}
])
->get()
->map(function ($item) {
if ($item->descriptions->isEmpty() === false) {
$item->description = $item->descriptions->first();
}
return $item;
});
and get to description:
foreach ($categories as $category) {
$description = $category->description;
}
You can't do that but you can use first() on a collection later, for example in a view:
#foreach ($categories as $category)
{{ $category->descriptions->first()->name }}
#endforeach
I can say to use instead of first() find() and give it the language_id or $default_language->id and this will try to find in the table first the column id and assign the value. If you have different id column name give it to the find like find(['your_column_name' => '<your_value']).
If you want array to something like ->toArray(). You can test different scenarios in tinker. php artisan tinker
Here is a link to document this -> https://laravel.com/docs/5.3/eloquent#retrieving-single-models

Resources