Calling searchable() on Query Builder based result throws errors - php

How to use searcable() method on results returned by query builder? Suppose there are five tables:
products
vendors
categories
vendor_products (pivot table for Products and Vendors)
product_categories (pivot table for Products and Categories)
Is there a way to use searchable on the following query:
return \DB::table("products")
->join('vendor_products', function ($join) {
$join->on('products.id', '=', 'vendor_products.product_id')
->MANY_OTHER_WHERE_CONDITIONS
})
->join('categories', function ($join) {
$join->on('category_id', '=', 'categories.id');
})
->MANY_OTHER_CONDITIONS
->searchable()
But Laravel Scout return error:
Call to undefined method Illuminate\Database\Query\Builder::searchable()
Is there a way to upload the results of above query?
I have added searcable trait on vendors, categories, and products models.
It looks like Scout only work when we have Eloquent relationship returned from the query and don't work on query builder.

Acording to Laravel Documentation
Laravel Scout provides a simple, driver based solution for adding full-text search to your Eloquent models.
Query builder doesn't have this functionality.
Instead, a non so fancy solution you could implement is to compare every column using WHERE LIKE. This supposes a more slowly functionality than Laravel Scope but can do the trick if you dont want to use Eloquent.
My Recomendation is to use Eloquent. Is a faster and easier way to work with databases while using laravel

Problem resolved, all credit goes to #kfirba. Copying his response from his blog
The searchable() method is only available for Eloquent’s query builder. Try converting your query above to use Eloquent. Change the DB::table(‘products’)->join... to Products::join...
You may need to change the “toSearchableArray()” method implementation to include your new fields that are not part of the Product model (vendor_quantity, vendor_price, etc.)

Related

Query More than one relationship from a model with() in Laravel

I need help to query a model base on its relationship:
I have a model called StoreStock, this model is related to a Product model, which has two model relationships, MasterList and Price.
I want to get all storeStock with two Product relationships.
I know i can do something like
StoreStock::all()->with('product.price')
->get()
With this, i can only pick either price or masterlist
Pass array of relationship to with method
StoreStock::with(['product.price','product.masterlist']) ->get()
A little bit explanation here, many of the Laravel methods which support string, also support arrays. You can hover over the specific method and get the intellisense. In your case, it can be written like:
StoreStock::with(['product.price','product.masterlist'])->get()
If you want to run a specific action over any specific relation, you can also write it like this:
StoreStock::with(['product.price' => function(Builder $query) {
// your action required with the query
},'product.masterlist']) ->get()
Hope someone finds this helpful

Laravel ORM: How to filter records from a table in a M:M relation using a condition of the other table?

I have those two tables: apps and categories, they are in a M:M relationship, I have the apps_categories table in place and the BelongsToMany() relations in their models, they work fine.
Now, in a form I'm displaying all the records in apps, but I want to filter based on category_id, so I have a $categories array where I store the filters.
My problem is solved using the DB facade doing like:
$apps = DB::table('apps')
->join('apps_categories', 'apps.id', '=', 'app_id')
->whereIn('category_id', $categories)
->select('apps.*')
->get();
So, I was wondering if there's a better way, using only the ORM. Thing is I have another table with a M:M relationship to apps, and I'm supposing using the ORM would be a better way to handle both relationships
You could use the whereHas method for this:
$apps = App::whereHas('categories', function ($query) use($categories) {
$query->whereIn('categories.id', $categories);
})->get();
Same as the above but uses an arrow function:
$apps = App::whereHas('categories', fn ($query) => $query->whereIn('categories.id', $categories))->get()
The 1st argument for whereHas is the method name you have used for the relationship in your model (categories) and
1st argument for the whereIn is the table name and field (categories.id).

Total count decreases in group by and sorting laravel app

I'm trying to build an application in Laravel 6.0 where I'm having a sorting functionality where some of the sorting is done by joining the table as it is relational data. My code is:
$query->join('project_attribute_relation', 'projects.id', '=', 'project_attribute_relation.project_id')
->join('project_attributes', 'project_attribute_relation.attribute_id', 'project_attributes.id')
->select('projects.*', 'project_attributes.name as categories_name')
->groupBy('projects.id')
->orderBy('categories_name', request('sort_by_column')['order']);
My problem is the paginated data i.e. counts of total project becomes less during this sorting as it always counts the existing relational data and empty relation is ignored. But I want to have those empty relation also as it is effecting my project functionality.
This is my general list of projects:
And this is my list when I do sorting with categories:
Because you are using Laravel's join(), that means you are using innerjoin to query, and the project with empty relation will be not included.
And if you want to display the project with empty relation,
you need to use leftjoin, so that the projects with empty relation will be included, code like this:
$query->leftjoin('project_attribute_relation', 'projects.id', '=', 'project_attribute_relation.project_id')
->leftjoin('project_attributes', 'project_attribute_relation.attribute_id', 'project_attributes.id')

laravel join with polymorphic pivot table using in on clause model names

I'm using eloquent polymorphic relationships that is awesome to manage pivot table between diferent models and other model called company.
I have a pivot table that contains the following structure:
I have to make a join query between vehicle table and pivot table using model_id and model_type using eloquent query builder. But when I do:
$builder->join('pivot_table', function($join){
$join->on('vehicle.id','=','pivot_table.model_id')
->on('pivot.model_type', Vehiculo::class );
})->select('vehicle.*',pivot_table.*)->get();
this code don't return any result. But if I change the second on clause to:
$builder->join('pivot_table', function($join){
$join->on('vehicle.id','=','pivot_table.model_id')
->on('pivot.model_type', 'like' , '%Vehiculo%');
})->select('vehicle.*',pivot_table.*)->get();
this code runs correctly and returns the results what I want, but I think that is the wrong way to obtain the results.
Somebody knows if there is a way make run the first code?
thanks for the responses.
Ok. I solved. The solution is changing the second on to where
$builder->join('pivot_table', function($join){
$join->on('vehicle.id','=','pivot_table.model_id')
->where('pivot.model_type' , Vehiculo::class);
})->select('vehicle.*',pivot_table.*)->get();

laravel Eloquent join and Object-relationship mapping

Ok so i'm kind of newish to eloquent and laravel (not frameworks tho) but i hit a wall here.
I need to perform some queries with conditions on different tables, so the eager load (::with()) is useless as it creates multiples queries.
Fine, let use the join. But in that case, it seems that Laravel/Eloquent just drops the concept of Object-relationship and just return a flat row.
By exemple:
if i set something like
$allInvoicesQuery = Invoice::join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();
and then looping such as
foreach ($allInvoicesQuery as $oneInvoice) {
... working with fields
}
There is no more concept of $oneInvoice->invoiceFieldName and $oneInvoice->contact->contactFieldName
I have to get the contacts fields directly by $oneInvoice->contactFieldName
On top of that the same named columns will be overwrited (such as id or created_at).
So my questions are:
Am i right assuming there is no solution to this and i must define manually the field in a select to avoid the same name overwritting like
Invoice::select('invoices.created_at as invoice.create, contacts.created_at as contact_create)
In case of multiple joins, it makes the all query building process long and complex. But mainly, it just ruins all the Model relationship work that a framework should brings no?
Is there any more Model relationship oriented solution to work with laravel or within the Eloquent ORM?
Instead of performing this join, you can use Eloquent's relationships in order to achieve this.
In your Invoice model it would be:
public function contact(){
return $this->belongsTo('\App\Contact');
}
And then of course inside of your Contact model:
public function invoices(){
return $this->hasMany('\App\Invoice');
}
If you want to make sure all queries always have these active, then you'd want the following in your models:
protected $with = ['Invoice']
protected $with = ['Contact'];
Finally, with our relationships well defined, we can do the following:
$invoices = Invoice::all();
And then you can do:
foreach($invoices as $invoice)[
$invoice->contact->name;
$invoice->contact->phone;
//etc
}
Which is what I believe you are looking for.
Furthermore, you can find all this and much more in The Eloquent ORM Guide on Laravel's site.
Maybe a bit old, but I've been in the same situation before.
At least in Laravel 5.2 (and up, presumably), the Eloquent relationships that you have defined should still exist. The objects that are returned should be Invoice objects in your case, you could check by dd($allInvoiceQuery); and see what the objects are in the collection. If they are Invoice objects (and you haven't done ->toArray() or something), you can treat them as such.
To force only having the properties in those objects that are related to the Invoice object you can select them with a wildcard: $allInvoicesQuery = Invoice::select('invoices.*')->join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();, assuming your corresponding table is called invoices.
Hope this helps.

Categories