Handling complex query in laravel 5 - php

I have simple task with complex query, 50% is done i need help for the rest of it.
Logic
I have landing pages where i choose category, then select
specifications under that category and products related to this two
filter will show in landing page. this is done
I also want have landing page when i select category without
choosing any specification and it show all products related to that
category regardless what specifications they have. need help for this
Codes
this is my controller i commented each part for better understanding
public function landings($slug){
$landing = Landing::where('slug', $slug)->Status('Active')->firstOrFail(); //find landing page
//getting landing pages category id (in case one landing page have several category involved)
$cat = DB::table('landing_categories')->where('landing_id', $landing->id)->get();
foreach ($cat as $key) {
$id[] = $key->category_id;
}
// i just add this to filter landing without specifications for if statement below **not sure about this yet**)
$spac = DB::table('landing_specifications')->where('landing_id', $landing->id)->get();
// this is where things happen (first $prod is responsible for landing with specifications and working fine, **else part** need fix to show landings without specifications.
if($spac != null){
//old
$prod = DB::table('landing_specifications')->where('landing_id', $landing->id)
->join('product_subspecification', function ($keys) {
$keys->on('product_subspecification.subspecification_id', '=', 'landing_specifications.subspecification_id');
})
->join('products', 'products.id', '=', 'product_subspecification.product_id')
->whereIn('products.category_id', $id)
->groupby('products.id')
->paginate(12);
}else{
//new
$prod = DB::table('landing_categories')->where('landing_id', $landing->id)
->join('products', 'products.category_id', '=', 'landing_categories.category_id')
->whereIn('products.category_id', $id)
->groupby('products.id')
->paginate(12);
}
return view('front.landing', compact('landing', 'prod'));
}
Hope my commenting in code above be useful to you to avoiding misunderstandings.
PS: I know I have 2 major issues here
My if statement isn't right
My else part need fix (but before fixing if() i am not able to see results of else part)
any idea?

SOLVED
I changed my if part to code below and now it is working perfectly
$spac = DB::table('landing_specifications')->where('landing_id', $landing->id)->first();
if($spac){
// rest of it...
hope it helps.

Related

Order by relevance based on multiple conditions using Laravel

I am having an issue while using order by based on multiple conditions.
User with most filled information should show up top and then the one's with less filled information.
$users = User::where('status',1)
->withCount('reviews')
->with('reviews','about')
->orderByRaw("CASE WHEN is_native != '0' AND photo != '' THEN 0 ELSE 1 END")// how i can match the about us relationship value here? means if user have added about intro then it should come first and reviews count?
->paginate(10);
Here is my About Relationship on User
public function about()
{
return $this->hasOne('App\UserAbout', 'user_id')->select('about');
}
NOTE: i am trying to do it with CASE, if there is any other good option you can please point out that.
Thank you
this means that you have to orderby about's count and then by review count, that will get the result you want:
$users = User::where('status',1)
->withCount(['reviews','about'])
->with('reviews','about')
->orderByRaw('about_count desc,reviews_count desc')
->paginate(10);
now user with 'about' will have about_count=1 others will have about_count =0
As #OMR suggested you can do that. But you don't need to use raw Query
$users = User::where('status',1)
->withCount(['reviews','about'])
->with('reviews','about')
->orderBy('about_count','desc')
->orderBy('reviews_count','desc')
->paginate(10);

Laravel: Best way to compare two collections: whereIn?

I have a table with categories, one table with products and another table products_user which tracks the products a user owns.
When displaying products on a page it should change a button from Buy to Bought if a user owns the product.
I get the products to display through $categories->products. What is the most efficient way to find out which of these products a user already owns?
I don't want to load the entire collection of the user owned products into the memory since these could be several thousands. I also don't want to create a Mysql query for each check.
There is an option for a wherein clause. But even then I am that there is a smarter way to create this clause without looping through every product to build an array.
Can someone help me to come up with a good logic? thank you
You can make use of Constraining Eager Loads to append more information to your products. In this case, the user_id is either NULL or user_id, meaning the user is bought the product or not.
$categories = Category::with(['products' => function ($q) {
$q->select(['products.*', 'user_id'])
->leftJoin('products_user', 'user_products.product_id', '=', 'products.id')
->where(function ($q) {
$q->whereNull('user_id')->orWhere('user_id', Auth::user()->id);
});
}])->get();
foreach ($categories as $category) {
$products = $category->products;
foreach ($products as $product) {
if (empty($product->user_id)) {
// user not yet bought the product
}
else {
// user already bought the product
}
}
}

filtering result using eager loading

I am creating a search in laravel for an API but my search gives me the wrong results. I am trying to search by location and food type. I have the following tables:
foods
shops
shop_food
users
comments
Here is my search code:
public function searchShop($food, $location)
{
//
if($food == " " || $location == " "){
return $this->index();
}
//get all records where city and food are equal to
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->with(['foods'=> function($query) use($food){
$query->where('name','=', 'fish pepper'); }])
->get();
//check if empty and return all
if($shops->isEmpty()){
return $this->index();
}
return $shops;
}
my result is the below instead of just the record where location and food it shows all the shops filtered by location even where food isnt a match :
The with method that you're using does not filter in the way that you think it does. Your code actually filters the food results, telling Eloquent to retrieve all Shop and either no foods, or the foods with the name fish pepper. This is called constraining eager loads.
The method you're looking for is whereHas rather than with. This is referred to as querying a relationships existence.
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->whereHas('foods', function($query) use($food){
$query->where('name','=', 'fish pepper');
})
->get();
This will now return only Shops that have a corresponding food entry with the name fish pepper.
If memory serves, whereHas won't actually populate foods for you, but in this instance you wouldn't need it, as it's safe to assume that they all have fish pepper. If you do want to pull all foods, change with('comments.user') to with(['comments.user', 'foods']).
Documentation for whereHas and other ways of achieving this can be found here.
Documentation about what you were doing with the with method can be found here.
Hope that helps.

How to represent averages and other aggregates in OOP?

I have a Model called Product. Product has among other things a price, a category and a created date.
I want to show a table showing the average price and average age by category in many places. Sometimes I want just one category, sometimes everything, sometimes only products that have been in stock for more than a certain time etc.
At the moment I'm using Laravel's Query Builder to generate those numbers in my controller, then passing that to a view.
To help try to reuse it, I have this in the methods before I need it:
$product_averages_base_query = DB::table('products')
->leftJoin('categories', 'products.MakeDescription', '=', 'categories.id')
->select(
DB::raw('count(products.id) as TotalNumber'),
DB::raw('AVG(Datediff("'.date('Y-m-d').'",products.created)) as AvgDaysInStock'),
DB::raw('AVG(Price) as AvgPrice')
);
Then when for the specific use case I use:
$averages = $product_averages_base_query->where('categories.name', '=', 'Example 1')->get();
or whatever the variant is.
This feels really "wrong" because I of course end up copying this code all over the place.
How do I represent this data in a way that will let me reuse it more easily? Should I have a class? What should it be called, and what's in it?
Should I have a Model somehow?
Any advice is welcome!
As for the appropriate place, you could very easily use query scopes and drop everything in your model. You'd probably want to start with your base query...
public function scopeBaseQuery($query)
{
return $query->leftJoin('categories', 'products.MakeDescription', '=', 'categories.id')
->select(
DB::raw('count(products.id) as TotalNumber'),
DB::raw('AVG(Datediff("'.date('Y-m-d').'",products.created)) as AvgDaysInStock'),
DB::raw('AVG(Price) as AvgPrice')
);
}
And then continue with the category name scope...
public function scopeOfName($query, $name)
{
return $query->where('categories.name', $name);
}
Add additional scopes as you need.
Then to use this, it would be quite easy...
$averages = Product::baseQuery()->ofName('Example 1')->get();
One solution would be to create an Eloquent class for the model and then put the functionality into a scope.
class Product extends Eloquent {
public function scopeAveragesByCategoryName($q, $catName) {
return $q->leftJoin('categories', 'products.MakeDescription', '=', 'categories.id')
->select(DB::raw('count(products.id) as TotalNumber'),
DB::raw('AVG(Datediff("'.date('Y-m-d').'",products.created)) as AvgDaysInStock'),
DB::raw('AVG(Price) as AvgPrice'))
->where('categories.name', '=', $catName);
}
}
$products = Product::averagesByCategoryName('Example 1')->get();
var_dump($products);
Or you could just as easily put the whole thing into a function.

How to restrict data by query in magento

I am new to magento and I am customizing some changes to product, catagory and home pages. I have writen the following Code to show all categories on home page
public function getRandomCategory()
{
$categoryCollection = Mage::getModel('catalog/category')
->getCollection()
->addAttributeToSelect('*');
$categoryCollection->getSelect()->order('RAND()');
return $categoryCollection;
}
How would i restrict the data by using a condition in case of * in ->addAttributeToSelect('*'); statement
A cool thing you can do to debug is to call
echo $categoryCollection->getSelect();
that will return the exact query that magento is generatingm now the addAttributeToSelect('*') what it does is to generate the 'Select * From ...' part of the query let's say that you only need to retrieve the category name
In that case you only need to do ->addAttributeToSelect('name') you_ can add multiple ->addAttributeToSelect('attribute') to retrieve multiple values.
Now if by restrict data you meant to only retrieve the categories WHERE something = tosomething else then you need to use addAttributeToFilter('atttribute', '')value
Check using_collections_in_magento for more information on the
Hope my answer helps

Categories