Use a scope in a JOIN in Laravel Eloquent - php

I am trying to get all the products with active prices and display them and the active price using a scope in the ProductPrices Model. My Products Model has an hasMany relation with prices:
Product.php (Model)
public function prices () {
return $this->hasMany(ProductsPrice::class);
}
My Prices Model has an scope is active that checks if the prices is active on this date:
ProductsPrices.php (Model)
public function scopeIsActive($query)
{
return $query->whereRaw(' timestampdiff(second, start_time, NOW()) >= 0')
->where(function ($query) {
$query->whereRaw(' timestampdiff(second, end_time, NOW()) <= 0')
->orWhereNull('end_time');
});
}
I tried many different ways to get products with an active price and display them both. Things I feel should work, but don't, are:
Route::get('/test', function (Request $request) {
return Product::join('products_prices', 'products.id', 'products_prices.product_id')
->prices->isActive()
->where('products.is_active', true)
->get();
});
I get the error:
Property [prices] does not exist on the Eloquent builder instance.
or test2
Route::get('/test2', function (Request $request) {
$prices = DB::table('products_prices')->select('id');
$product = Product::whereIn('id', $prices)->get();
return $product->prices()->isActive()->get();
});
I get the error:
Method Illuminate\Database\Eloquent\Collection::prices does not exist.
Why can't I access the ->prices() on my Product Model? Should I not use eloquent for this and go for the Query Builder of Laravel?

I think a combination of with and whereHas might work:
$products = Product::with(['prices' => function($query) {
$query->isActive(); // to fetch prices relation that is active
}])->whereHas('prices', function($query) {
$query->isActive(); // this one is to filter the products that has active price
})->get();

Related

laravel 8 Eloquent query builder

I'm making an attempt to write Eloquent join query but i didn't get the result i wanted.
i have 2 table
products which contains all information about products
category_product contains product_id and category_id
i just want to select all information about products which their category_id is equal to 2
After you have belongsToMany relation between categories and products.
Product::whereHas('categories', function($query){
$query->where('id',2);
})->with('categories')->get();
I can offer you a solution like this
class Product extends Model
{
public function category()
{
return $this->morphToMany(Category::class, 'category_product table');
}
}
class Category extends Model
{
public function product()
{
return $this->morphedByMany(Product::class, 'category_product table');
}
}
in Controller
$products = DB::table('products')
->join('category_product', 'products.id', '=', 'category_product.products_id')
->select('products.*')
->where('category_product.category_id','=',2)
->get();

Laravel Eloquent Fetch all rows and their respective pivot table rows

I have a table of orders, which contain line items, which are stored in a pivot table.
Once all of the line items have been successfully processed, the order will be marked as "processed" and needs to be displayed on the page.
I therefore want to fetch all orders which have been marked as "processed" as well as the line items which are included in their respective orders.
My query looks like this:
$orders = DB::table('order_product')
->join('products', 'order_product.product_id', '=', 'products.product_id')
->join('orders', 'order_product.order_id', '=', 'orders.order_id')
->join('customers', 'orders.customer_id', '=', 'customers.customer_id')
->where('order_product.variation_status', '=', 'dispatch')
->where('orders.store', '!=', 'null')
->groupBy('order_product.order_id')
->get();
return response()->json($orders);
My thinking is to fetch all the pivot table items which are processed and then group the result by order_id but this unfortunately doesn't work.
I receive the following output:
Unfortunately the variation property only contains one line item from the pivot table, instead of two.
Can somebody help as to what I might be doing wrong?
UPDATE
Here are my models
Order.php
/**
* The products that belong to the Order.
*/
public function products()
{
return $this->belongsToMany('App\Product','order_product','order_id','product_id')
->withPivot(['qty', 'variation', 'variation_status'])
->withTimeStamps();
}
/**
* The customer that belongs to the Order.
*/
public function customer()
{
return $this->belongsTo('App\Customer', 'customer_id');
}
Product.php
/**
* The orders that belong to the product.
*/
public function orders()
{
return $this->belongsToMany('App\Order')
->withPivot(['qty', 'variation_status'])
->withTimeStamps();
}
I can't guarantee that this is correct right away because I normally don't work with ->withPivot and have no test environment running yet so this is from the top of my head at the moment. But this might give you an insight of how to handle this usecase.
Let's start with order as a base
Order::get();
Now let's extend this functionality to retrieve an order with a customer and products
Order::with('customer', 'products')->get();
What we've to do now is resolving your where conditions in the above eloquent query:
->where('order_product.variation_status', '=', 'dispatch')
->where('orders.store', '!=', 'null')
What you could do is the following:
Order::with(['products' => function($query){
$query->where('variation_status', 'dispatch');
}, 'customer'])
->where('store','!=','NULL')
->get();

Laravel Lucid whereHas by latest column in pivot table

I have 3 tables : Orders ( id, name, surname, created_at and updated_at ), OrdersStatuses (order_id, order_status_name_id, created_at) and OrderStautsNames ( id and name ):
I have orders model which has method like this:
protected $appends = ['actual_status'];
public function orderProducts()
{
return $this->hasMany(OrderProduct::class);
}
public function statuses()
{
return $this->belongsToMany(OrderStatusName::class, 'order_statuses')
->withPivot('created_at');
}
public function getActualStatusAttribute()
{
return $this->statuses()->latest('set_at')->first();
}
I have problem with seraching all orders, where highest status id ( this info is in pivot table OrderStatuses, and I need name of this status which is in OrderStatusNames ) is like $statuses (this is an array with statuses ). I started do like this:
$orders = Order::query();
$orders->whereHas('statuses', function($query) use ($statuses) {
$query->whereIn('order_status_name_id', $statuses);
});
But it return me orders with not latest status, but this query search me throught all statuses... . Can i do this by latest status? I tryed to use current_status but I don't know how to get there.
Add this relation to your Order Model
public function lastStatus()
{
return $this->hasOne(OrderStatusName::class)->select('OrderStautsNames.*', 'order_statuses.order_id', DB::raw('MAX(order_statuses.set_at) as lastOrder'))->join('order_statuses', 'order_statuses.order_status_name_id', '=', 'OrderStautsNames.id')
}
Then eager load it
$orders = Order::whereHas('lastStatus', function ($query) use ($statuses) {
$query->whereIn('OrderStautsNames.id', $statuses);
})->get();

Laravel Eloquent: how to filter reviews of products by product category and/or product brand?

I will describe this situation with more details:
I have a list of products where every product belongs to a certain category and to a certain brand. Some of the products can get reviewed by users.
On /reviews/ page in my Laravel application, I have a list of reviews and select boxes for category and brand along with search button of course.
If user doesn't choose category or brand, all reviews get displayed, paginated, and that's good.
The problem arises when user chooses either category or brand or both and tries to get all the reviews filtered that way.
Reviews table fields: ID, user_id(foreign key users.ID), product_id(foreign key products.ID), text
Products table fields: ID, category_id(foreign key categories.ID), brand_id(foreign key brands.ID), name
Categories table fields: ID, name
Brands table fields: ID, name
Users table fields: ID, username
When I'm listing reviews, I'm simply using:
$reviews = Review::orderBy('id', 'DESC')->paginate(5);
If I would like to filter reviews by user_id, that would be easy as the reviews table contains user_id column,
but, how to filter them by product category and/or product brand?
Here are Review, Product and Category models:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model {
protected $fillable = [];
public function product() {
return $this->belongsTo('App\Product');
}
public function user() {
return $this->belongsTo('App\User');
}
}
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
protected $fillable = [];
public function user() {
return $this->belongsTo('App\User');
}
public function reviews() {
return $this->hasMany('App\Review');
}
public function category() {
return $this->belongsTo('App\Category');
}
}
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model {
protected $fillable = [];
public function products() {
return $this->hasMany('App\Product');
}
}
If I use joins, then $review->user->id, $review->user->username, $review->id are not correct, I'm getting reviews.product_id as $review->id, and products.user_id as $review->user->id in blade template.
I was trying this join variant:
$reviews_query = Review::orderBy('reviews.id', 'DESC')
->Join('products', 'reviews.product_id', '=', 'products.id')
->Join('categories', 'products.category_id', '=', 'categories.id')
->Join('brands', 'products.brand_id', '=', 'brands.id')
->Join('users', 'reviews.user_id', '=', 'users.id')
->where('reviews.active', '=', 1)
->GroupBy('reviews.id')->paginate(5);
And this for filtering by category_id:
if (Input::has('category_id')){
$category_id = Input::get('category_id');
$reviews_query->where('categories.id', $category_id);
}
I'm not sure how to correctly address ambiguous ids such us product_id, review_id, user_id in blade template ($review->id, $review->user->id, all are messed up mutually)
Add hasManyThrough relationship in your category model
public function reviews() {
return $this->hasManyThrough('App\Review', 'App\Product');
}
now you can have all reviews by a category like this
$category->reviews();
you can add other query clauses to it like
$category->reviews()->orderBy('id', 'DESC')->paginate(5);
Try this,
$reviews = DB::table('review')
->join('product', 'product.id', '=', 'review.product_id')
->Join('category', 'product.category_id', '=', 'category.id')
->orderBy('id', 'DESC')->paginate(5);
for more you can visit-
Laravel join with 3 Tables
This may work for you.....
filter all reviews by a specific CategoryID then simply use ->
$categoryID = 1;
Categories::with(['products' => function($product){
$product->with('reviews');
}])->where('id', '=', $categoryID)->get();
also filter all reviews by a specific BrandID then
$brandID = 1;
Brands::with(['products' => function($product){
$product->with('reviews');
}])->where('id', '=', $brandID)->get();

Order by Votes in Laravel ORM

I have 3 Models... Category, Post, Vote
When viewing a category, I am showing an index of all Posts in that category. So I'm doing something like foreach ($category->posts as $post) in my view.
The question I have now is how can I order the posts based on the sum of votes they have?
I have standard relationships setup, so that a post hasMany votes.
You can do it either by defining a helper relation on the Post model and sorting the collection after the relation is loaded OR simply by joining the votes and ordering in the query.
1 RELATION
// Post model
public function votesSum()
{
return $this->hasOne('Vote')->selectRaw('post_id, sum(votes) as aggregate')->groupBy('post_id');
}
// then
$category->posts->load('votesSum'); // load relation on the collection
$category->posts->sortByDesc(function ($post) {
return $post->votesSum->aggregate;
});
// access votes sum like this:
$category->posts->first()->votesSum->aggregate;
2 JOIN
$category->load(['posts' => function ($q) {
$q->leftJoin('votes', 'votes.post_id', '=', 'posts.id')
->selectRaw('posts.*, sum(votes.votes) as votesSum')
->groupBy('posts.id')
->orderBy('votesSum', 'desc');
}]);
// then access votes sum:
$category->posts->first()->votesSum;
You can use scope for that:
// Post model
public function scopeOrderByVotes($query)
{
$query->leftJoin('comments','comments.post_id','=','posts.id')
->selectRaw('posts.*,sum(comments.id) as commentsSum')
->groupBy('posts.id')
->orderBy('commentsSum','desc');
}
// then
$category = Category::with(['posts' => function ($q) {
$q->orderByVotes();
}])->whereSlug($slug)->firstOrFail();

Categories