How to call two model method in my controller in Laravel - php

I hope you can understand the question, the fact is that I have created two methods in my model, one used to filter records by category and the other by city.
But when I go to my controller, in the public function index, I think it can't call both at the same time, I hope it is better understood in the example.
public function **scopeBuscadorCategoria** (Builder $query) {
if (session()->has('s')){
$query->where('nombre', 'LIKE', "%". session('s') ."%");
}
if (session()->has('dcategoria')){
$query->where('categoria_id', session('dcategoria'));
session()->remove('dcategoria');
}
return $query->paginate();
}
public function **scopeBuscadorLocalidad** (Builder $query) {
if (session()->has('dlocalidad')){
$query->where('localidad_id', session('dlocalidad'));
session()->remove('dlocalidad');
}
return $query->paginate(21);
}
As you can see, those are the two methods that I have created in the model.
public function index (){
**$destinos = Destino::buscadorcategoria();**
$categorias = Categoria::paginate(6);
$localidades = Localidad::all();
return view('destinos.index', compact('destinos', 'categorias', 'localidades'));
}
And I think here is the problem, in the controller, "$destinos = Destino::buscadorcategoria();", I can only put 1 method, in this case it filters records of the categories, but if I need to filter by city, I must delete the method of categories and put the city, something like "$destinos = Destino::buscadorlocalidad();"
Can you call two methods in the variable $destinos? What could you do in this case? I need to filter by category and by city.
I hope you can help me, thank you all very much.

Your scopes shouldn't return "paginate" but only the query :
public function scopeBuscadorCategoria (Builder $query) {
if (session()->has('s')){
$query->where('nombre', 'LIKE', "%". session('s') ."%");
}
if (session()->has('dcategoria')){
$query->where('categoria_id', session('dcategoria'));
session()->remove('dcategoria');
}
return $query;
}
public function scopeBuscadorLocalidad (Builder $query) {
if (session()->has('dlocalidad')){
$query->where('localidad_id', session('dlocalidad'));
session()->remove('dlocalidad');
}
return $query;
}
And in your controller :
public function index (){
$destinos = Destino::buscadorcategoria()->buscadorLocalidad()->paginate(21);
$categorias = Categoria::paginate(6);
$localidades = Localidad::all();
return view('destinos.index', compact('destinos', 'categorias', 'localidades'));
}

Related

Filtering Laravel resource collections is there a better way

I am experimenting with creating a Restful API in Laravel and I am making use of the resources feature designed for API output. I have created the following models:
Book, Author and Category. I also created a resource for each of these models.
There is a one to many relationship between author and book and a many to many between category and book which has a pivot table.
I can return a collection of all books easily with the following:
return BookResource::collection(Book::with(['author', 'categories'])->paginate(10));
But I want to easily filter by author and category so I have implemented it in the following way inside my controller:
public function index(request $request)
{
//if no filer params are passed in return all books with author and categories
if (!$request->has('author') && !$request->has('category')) {
return BookResource::collection(Book::with(['author', 'categories'])->paginate(10));
}
//author param is passed in
if($request->has('author') && !$request->has('category')){
$authorName = $request->author;
return BookResource::collection(Book::whereHas('author', function ($query) use ($authorName) {
$query->where('name', $authorName);
})->get());
}
//category param is passed in
if(!$request->has('author') && $request->has('category')){
$categoryName = $request->category;
return BookResource::collection(Book::whereHas('categories', function ($query) use ($categoryName) {
$query->where('name', $categoryName);
})->get());
}
}
Is there a better more efficient way of returning the BookResource collection filtered by author and category?
Please try to implement this way. Hope this helps. Thanks.
public function index(){
$author = request ('author', null);
$category = request ('category', null);
$books = Book::with(['author', 'categories'])->when($author, function ($query) use ($author) {
return $query->whereHas('author', function ($query) use ($author){
$query->where('name', $author);
});
})->when($category, function ($query) use ($category) {
return $query->whereHas('categories', function ($query) use ($category) {
$query->where('name', $category);
});
})->paginate(10);
return BookResource::collection($books);
}

Laravel Eloquent deep nested query

I'm still learning Laravel and I can't find the solution for this problem.
I need to get invoices(with expenses) that are related to specific Partner Type.
I tried this:
$p = Project::with(['invoices.partner.partnerType' => function($query){
$query->where('partnerTypeName', 'Lieferant');
}, 'expenses'
])->where('id', $id)
->first();
I want to select invoices for Lieferant, but I get all invoices for one project.
Project Model:
public function invoices()
{
return $this->hasMany('App\Invoice');
}
Invoice Model
public function expenses()
{
return $this->hasMany('App\Expense');
}
public function partner()
{
return $this->belongsTo('App\Partner');
}
Partner Model
public function partnerType()
{
return $this->belongsTo('App\PartnerType');
}
Edit: PartnerType Model
public function partners()
{
return $this->hasMany('App\Partner');
}
Edit 2: Database
Partner(partnerID, name, partnerTypeId)
PartnerType(partnerTypeId, partnerTypeName)
Project(projectID, name)
Invoice(invoiceID, name, projectID, partnerID)
Expenses(expenseID, invoiceID)
If your models look like that.
Should be like :
$p = Project::with(['invoices' => function($query){
$query->where('partnerTypeName', 'Lieferant')
->with(['expenses','partner' => function($q){
$q->with('partnerType');
}]);
}])->where('id', $id)
->first();
return dd($p);
The solution to your problem is to update your query like this:
$p = Project::with(['invoices' => function($query){
$query->with('expenses')->whereHas('partner.partnerType', function($q){
$q->where('partnerTypeName', 'Lieferant');
});
}])
->where('id', $id)
->first();
But a cleaner solution would be using a scope for your problem.
In your Invoice model.
// Invoice.php
public function scopeByPartnerType($query, $partnerType)
{
$query->whereHas('partner.partnerType', function($q) use ($partnerType) {
$q->where('partnerTypeName', $partnerType);
});
}
And then in your Project model, add another relation that will just get Invoices with a particular partner type.
// Project.php
public function lieferantInvoices()
{
return $this->hasMany('App\Invoices')->byPartnerType('Lieferant');
}
Now you can do just this:
$project->find($id)->load('lieferantInvoices');

Laravel relationships belongsToMany

I want to fetch all orders and connect them with their statuses, but I want only the last status for each. I was trying to do it like this, but I only get the status for the first order the rest doesn't have any. How can I fix it?
static function actualKioskOrders()
{
$orders = Order::query();
return $orders->with([
'statuses' => function ($query) {
$query->orderBy('created_at', 'desc')->first();
}
]);
}
In order model add this method, I suggest doing query on model class.
public function status(){
return $this-> belongsToMany(Status::class);
}
public function latestStatus()
return $this->status()->latest()->first();
}
.
I believe the easier way to do it would be
$orders = Order::with('status')->orderBy('created_at', 'desc');
Then if you dd($orders); you will have all the orders with their corresponded status.

Eloquent Query Scope on Relationships

I have two models, App\Song (belongsTo App\Host) and App\Host (hasMany App\Song).
I have the following query in my Controller:
$songs = Song::whereHas('host', function($query) {
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'))
->where('active', 1);
})
->whereNull('downloaded')
->get();
For reusability I would like to turn into a query scope(s).
I'm quite new to Eloquent so I'm not sure this is the correct way to do this being that its two Models as its not returning any results (where there should be).
Song.php
public function scopeEligable($query)
{
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'));
}
public function scopeActiveHost($query)
{
$query->where('active', 1);
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}
You should put scopes into Models they belong to. Looking at your initial query scopes scopeEligable and scopeActiveHost belongs to Host model, so you should move them into Host model and then you'll be able to use your query using scopes like this:
$songs = Song::whereHas('host', function($query) {
$query->eligable()->activeHost();
})->inDownloadedQueue()->get();
and as already pointed in comment you should add return to each scope so they could be used as they intended.
EDIT
If you would like to make using it shorter, you could create new relationship in Song model:
public function activeHost()
{
return $this->belongsTo(Host:class)->eligable()->activeHost();
}
so now, you could write:
$songs = Song::whereHas('activeHost')->inDownloadedQueue()->get();
I think you're mistaken about 2 models. I think this should work
Song.php
public function scopeEligable($query, $active) {
return $query->whereHas('host', function($q) {
$q->where('skip_threshold', '>', \DB::raw('songs.attempts'))->where('active', $active);
})
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}
Usage
$songs = Song::eligable(true)->inDownloadQueue()->get();

Laravel - search by title?

How to search by title in the ServiceType only? There is also a title field in the Package which should be avoided
For example, in the Model:
class Package extends Eloquent {
protected $table = 'package';
function serviceType()
{
return $this->belongsTo('ServiceType');
}
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service);
}
}
Note:
There is a service_type_id field in the Package and id, title fields in the serviceType
in the controller:
$packages = Package::getPackagesByServiceType('something')->get();
No result appeared for some reason? It should search for something in the serviceType
It seem it wouldn't work to combine with() and where(). When I remove the where() and it work.
You can't use where() like that to filter by a related model. You should use whereHas() instead:
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->whereHas('serviceType', function($q) use ($service){
$q->where('title', '=', $service);
});
}
Note if you don't need serviceType in the packages afterwards you don't have to eager load it, ergo you can remove the with('serviceType')
Also if you call get() in the controller you should use a query scope. It offers the same functionality but it's not a static function and it's the Laravel way
public function scopeByServiceType($query, $service){
return $query->with('serviceType')->whereHas('serviceType', function($q) use ($service){
$q->where('title', '=', $service);
});
}
And you use it like this:
$packages = Package::byServiceType('something')->get();
class Package extends Eloquent {
protected $table = 'package';
function serviceType()
{
return $this->belongsTo('ServiceType');
}
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service)->get();
}
}
You forgot the ->get();
The ->get() should be in the Model
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service)->get(); // here
}
and in the controller it should be like this:
$packages = Package::getPackagesByServiceType('something');
Hope that helps... I had similar issues in my Model - Controller structure....

Categories