Laravel: how to filter eloquent data by relationships - magento

I'm building a product catalog in laravel and I would like to filter the products based on their attributes.
I have 2 tables, Products & Attributes. Attributes have a key, value and product_id. For example:
product_id - key - value
12 - brand - BestBrandEver
23 - brand - EvenBetterBrand
Now I would like to be able to filter down my products via a form on the category page which passes query string parameters like "brand=BestBrandEver or brand=EvenBetterBrand" and retrieve only the products from that brand. Eventually I would like to do the same with color, material etc... It's much like Magento's or Woocommerce layered navigation.
I'm stuck for a while now because I don't know how to start building this the right way. Is there someone who could help me with this or is able to point me in the right direction (maybe even with a tutorial or video)?
Thanks in advance!

Assuming a url like this:
/products?filters[brand][]=a&filters[brand][]=b&filters[color][]=a&filters[color][]=b
and so forth...
Use the whereHas method to restrict your products by their attributes:
Product::whereHas('attributes', function ($query) {
foreach (Input::get('filters') as $key => $values) {
$query->orWhere(function($query) use ($key, $values) {
$query->where('key', $key)->whereIn('value', $values);
});
}
});

Related

WinterCMS/ OctoberCMS builder - sorting product in categories

I built the plugin (with builder) where I have products and categories. Product and categories are "conected" by relation table. I have list of products and another list of categories. On page where I list my all categories everthing works fine. The problem is with the single category view. Now products in a single category are listed by add order. But I want to have my own order of products or some sort of reordering. Builder delivers sorting in record list (categories page) but not in record details (single category page).
Thanks for answers.
In your model, you have already added relationship like category <-> products
Here you can add an order option to set the order.
// inside category model
public $hasMany = [
'products' => [
\Acme\Shop\Models\Product::class,
'order' => 'title desc', // <- here although its hardcoded
]
];
Or if you prefer dynamic order then inside your page code section you can add the onStart function and fetch the product manually.
function onStart() {
$id = 1; // just for example you can get category based on slug
$sortedProducts = Category::find($id)
->products()->orderBy('title')->get();
// dynamic as per your need ^
$this['products'] = $sortedProducts;
}
// now in the code section you can use `products` and they will be sorted
if any doubt please comment

Assigning ids to relationships in Laravel

I have an eloquent model called "Customer". Customers have a one-to-many relationship with another model, "Image". I want to group the images owned by a customer so that I can assign them to different usecases - a customer would have a single image for a Logo, for instance, but many images for their gallery section.
How do I assign a usecase to a relationship in Laravel?
I'm thinking I will make an intermediary model called 'Usecase' and then access the images using the hasManyThrough method. Many relationships in my system need their usecases defined. I'm new to laravel, is this something that is already covered in the framework?
EDIT:
I also want to have Images assigned to Customers when they are not in use - so I could upload a bunch of images and say that they belong to a particular customer, but they aren't used anywhere until we assign a 'usecase' (ie, 'logo' or 'gallery'). This way I would have an image gallery of all the images that are assigned to the customer, and then a gallery for images assigned as 'gallery'. Bleh, does that make sense?
There are 3 approaches that I can think of to accomplish this.
1: You turn 'Logo' into a column on 'Customer' rather than a separate model and keep 'Image' for all gallery images.
2: You turn 'Logo' into a separate model, same for 'Customer' and 'Image'
3: You complicate your life and keep 'Logo' and gallery images as 'Image', then add a way to distinguish between the two (perhaps a flag: is_logo or an enum column: image_type).
If you ask me, the 2nd option is the most appealing.
You may use Polymorphic Relationships relationship for this or separate the 2 images table
Customer
Logo
Image
Customer can have 1 logo
Customer can have 1 or many Images
$customer = new Customer;
$customer->with('images', 'logo')->get();
The will help you query in a group of result for all of your images and logo.
You could use 2 relations for the same model.
Model:
class Customer extends Model
{
public function gallery()
{
return $this->hasMany(Image::class)->where('type', 'gallery');
}
public function logo()
{
return $this->hasOne(Image::class)->where('type', 'logo');
}
}
Retrieving:
$customer = Customer::with(['gallery', 'logo'])->get();
Outputing:
#foreach($customer->gallery as $image)
{{ $image->some_attribute }}
#endforeach
{{ $customer->logo->some_attribute }}
I think the cleanest approach is to...
1) Have a "UseCase" table that can define all types of use cases. This allows for simple scalability and readability without cluttering the image table with a bunch of new VARCHARs or whatever.
2) Create a "use_case" column in the "Image" table and assign the corresponding ID from the "UseCase" table above per image.
3) Create a hasOne/belongsTo relationship between "Image" and "UseCase" on UseCase.id = Image.use_case
4) Create a scope in your "Customer" table something like below...
public function scopeUseCase($query, $case){
$query->with(['Image.UseCase' => function($relationQuery) use ($case) {
return $relationQuery->where('name', '=', $case);
}]);
}
5) Finally, reference the cases by name in queries using the scope like so...
Customer::useCase('logo')->get();
Should be a pretty clean and straightforward process from there. Sorry for formatting issues stuck on mobile.

Laravel nova overriding/setting custom value for the Fields or Has Through relationship

I am developing a Web application using Laravel. For the admin panel, I am using Laravel Nova. What I am trying to do now is that I need to use data from the table which has relationship through another table. To be, clear, see my database structure below.
items
=====
id
name
price
sub_category_id
sub_categories
==============
id
name
parent_category_id
parent_categories
=================
id
name
What I am trying to achieve inside the Nova is that I want to display the parent category name of the item on the item index/list page. The first thing is that I do not want to create custom attribute something like this in the model
protected $appends = [
'parent_category_name'
];
function getParentCategoryNameAttribute()
{
//code here
}
Therefore, there are two solutions I can think of. The first solution is using the HasThrough relationship. But I cannot find it in Nova. So, I cannot use it. The second solution is that overriding the field value on render. Something like this.
Text::make("fieldname")->fillUsing(function($request, $model, $attribute, $requestAttribute) {
//$model->sub_category->parent_category - then I can return a value
return "Parent category name";
})->onlyOnIndex()
But the above code is not working. So, what would be the best approach to handle the has-through relationship in Nova?
Assuming you have defined the relationship sub_category & parent_category properly.
Define the relationship in Item model as below
public function parent_category()
{
return $this->sub_category->parent_category();
}
Then use it in Item resource as below.
BelongsTo::make('Parent Category')
->hideWhenCreating()
->hideWhenUpdating(),

SEO friendly URLs with category/subcategories/article slug? [Laravel]

First of all, I have Article model and articles table in the database. Each article can be shown using Laravel's standard URI structure: www.example.com/articles/5 (where 5 the article id.). Each article has a slug field (slug column in the articles table) , so with Route Model Binding it is easy to change this and have a slug instead of id in the URI:
In RouteServiceProvider.php I just added:
public function boot(Router $router)
{
parent::boot($router);
\Route::bind('articles', function($slug) {
return \App\Article::where('slug', $slug)->firstOrFail();
});
}
... and now I can open articles with: www.example.com/articles/this-is-some-slug .
On the other hand, each article belongs to one category. For example, let's say that there are the following categories:
Politics
Sport
Football
Tennis
ATP
WTA
Culture
I created these categories by using Baum (an implementation of the Nested Set pattern for Laravel 5's Eloquent ORM). So there is a Category model and categories table in the database:
$table->increments('id');
$table->string('name');
$table->integer('parent_id')->nullable();
$table->integer('lft')->nullable();
$table->integer('rgt')->nullable();
$table->integer('depth')->nullable();
$table->timestamps();
Of course, in articles table there is a column category_id because of One-to-Many relationship (one Article belongs to one Category, one Category can have many Articles).
All articles belonging to some category can be displayed via the following URL: www.example.com/articles/category/1 (where 1 is the id). If we add slug column to the categories table & set Route Model Binding :
\Route::bind('category', function($slug) {
return \App\Category::where('slug', $slug)->firstOrFail();
});
then we will use a slug instead of id: www.example.com/articles/category/politics (this will display all the articles belonging to the category politics).
But I would like to have URIs with the following structure:
www.example.com/sport/tennis/wta/article_slug (/category/subcategory/subcategory/article_slug)
www.example.com/politics/article_slug (/category/article_slug )
and so on...
The problem is that I have no idea how to do this with Laravel. Is it even possible? How would you solve this problem?
Thanks in advance and sorry for my bad English.
SEO friendly URLs with category/subcategories/article slug?
To produce a url that's example.com/category/subcategory/article is pretty simple, but you're obviously looking to add the complexity of multiple subcategories. For that we'll need to look at route parameter regex constraints.
Using regex in your route parameter constraints you can get the subcategories as a string eg. subcategory1/subcategory2 and then pass it via another custom binding or directly to your controller.
Here's an example of the route you need with the route parameter constraint added:
// Route to closure
Route::get('/{category}/{subcategories}/{article}', function($category, $subcategories, $article)
{
return $subcategories;
})->where('subcategories', '(.*)');
// Route to controller method
Route::get('/{category}/{subcategories}/{article}', 'ArticlesController#show')->where('subcategories', '(.*)');
And an example of a custom binding for your subcategories parameter that returns the subcategories as an array:
$router->bind('subcategories', function ($value) {
return explode('/', $value);
});
Caveat: The only problem you will run into using the route parameter bindings in the way you've described, is that the article loaded here is only dependant on the slug being correct. It will still load if the categories in the url are unrelated, which you'll need to take care of in your controller logic.

Replace the column name in the magento collection while loading it

I have a custom module and I am loading a collection like following
$collection = Mage::getModel('module/product')->getCollection()
->addFieldToFilter('sku',$sku);
There is field named as prod_id in the database. Can I get this as entity_id while loading the collection?
If yes. Please help how to do this.
First of all all
addAttributeToFilter() is used to filter EAV collections.
addFieldToFilter() is used to filter Non-EAV collections.
EAV-models are for example product, customer, sales, etc so you can use use addAttributeToFilter() for those entities.
addFieldToFilter() is mapped to `addAttributeToFilter()` for `EAV` entities. So you can just use `addFieldToFiler().`
You can have a look in app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php where the Mapping is done:
public function addFieldToFilter($attribute, $condition = null) {
return $this->addAttributeToFilter($attribute, $condition);
}
If you are using custom module then you can directly use addFieldToFilter() with your column name
Like
$collection = Mage::getModel('module/model')->getCollection()
->addFieldToFilter('column_name',$data);
Let me know if you have any query

Resources