How to properly connect models and custom classes in Laravel - php

I do not understand how to properly connect the model and filter. I implemented this using Dependency Injection in the controller, but I don’t like the fact that it needs to be done in every method where filtering should be applied. It would be very convenient if the model itself understood which class with filters to use.
Tell me how to do better.
Filtration Class:
namespace App\Classes\Filter;
class QueryFilter
{
protected $query;
protected $params;
public function apply($query, $params)
{
$this->query = $query;
$this->params = $params;
foreach ($this->filters() as $filter => $value){
if(method_exists($this, $filter)){
$this->$filter($value);
}
}
return $this->query;
}
public function filters()
{
return $this->params;
}
}
Heirs implement filters for different models:
namespace App\Classes\Filter;
class PositionFilter extends QueryFilter
{
public function title($value)
{
$this->query->where('title', 'LIKE', "%$value%");
}
}
class GasStationFilter extends QueryFilter
{
public function number($value)
{
$this->query->where('number', 'LIKE', "%$value%");
}
public function region($value)
{
$this->query->whereHas('region', function ($query) use ($value){
$query->where('regions.id', $value);
});
}
}
In the controller, I inject the desired class with filters and apply filtering like this (I use scope in the model):
public function index(GasStationIndexRequest $request, GasStationFilter $filters)
{
$gasStations = GasStation::with('region')
->filter($request->validated(), $filters)
->take(10)
->get();
return GasStationSelect2Resource::collection($gasStations);
}
Model:
namespace App\Models;
class GasStation extends ListModel
{
public function region(): BelongsTo
{
return $this->belongsTo(Region::class);
}
public function scopeFilter($query, $params, $filters) : Builder
{
return $filters->apply($query, $params);
}
}

Related

Get relationship data from eloquent builder laravel 7

I wanna filter my table data. I have a table for states, one for cities and one for students.
states(id, name)
cities(id, name, state_id)
students(id, first_name, last_name, city_id)
When I wanna filter data with eloquent builder how can I access relationships for handle state filter.
<?php
namespace App\StudentSearch\Filters;
use Illuminate\Database\Eloquent\Builder;
class StateId implements Filter
{
/**
* #inheritDoc
*/
public static function apply(Builder $builder, $value)
{
// something like this
return $builder->where('state_id', $value);
}
}
StudentSearch.php
<?php
namespace App\StudentSearch;
use App\Student;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
class StudentSearch
{
public static function apply(Request $filters)
{
$query = static::applyDecoratorsFromRequest($filters, (new Student)->newQuery());
return static::getResults($query);
}
private static function applyDecoratorsFromRequest(Request $request, Builder $query)
{
foreach ($request->all() as $filterName => $value) {
$decorator = static::createFilterDecorator($filterName);
if (static::isValidDecorator($decorator)) {
$query = $decorator::apply($query, $value);
}
}
return $query;
}
private static function createFilterDecorator($name)
{
return __NAMESPACE__ . '\\Filters\\' . Str::studly($name);
}
private static function isValidDecorator($decorator)
{
return class_exists($decorator);
}
private static function getResults(Builder $query)
{
return $query->get();
}
}
Note that relations between tables is completely defined in models.
I found the solution.
We can do this with eloquent builder:
$builder->whereHas('relationName', function($query) use($value) {
$query->where('state_id', $value);
});
Link to source

How can I filter this with eloquent

I try to filter SurveyQuestionnaire which have Answer = 'poor' and their Questionnaire have step = 'rating'.
I've tried to look through the documentation of Eloquent and I've found nothing help.
This is my models.
class Questionnaire extends Model {
...
public function surveysQuestionnaires() {
return $this->hasMany(SurveyQuestionnaire::class, 'question_id');
}
public function answers() {
return $this->hasMany(Answer::class, 'question_id');
}
public function questionnaires() {
return $this->hasMany(QuestionnaireTranslation::class, 'question_id' );
}
}
class SurveyQuestionnaire extends Model {
public function survey() {
return $this->belongsTo(Survey::class ,'survey_id');
}
public function questionnaires() {
return $this->belongsTo(Questionnaire::class, 'question_id');
}
public function answer() {
return $this->belongsTo(Answer::class, 'answer_id');
}
}
Well, the hasMany method returns query builder, so you can simply add some conditions:
public function surveysQuestionnaires() {
return $this->hasMany(SurveyQuestionnaire::class, 'question_id')->where('Answer', 'poor');
}
Also, you can read this link Constraining Eager Loads and add your conditions manually after taking an instance of your model:
$items = App\Questionnaire::with(['surveysQuestionnaires' => function ($query) {
$query->where('Answer', 'poor');
}])->get();

Laravel 5.4 can't get relation of elloquent model

I have filters which belong to filter groups
Filter.php:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Filter extends Model
{
public function group()
{
return $this->belongsTo('App\FilterGroup');
}
public function products()
{
return $this->belongsToMany('App\Product', 'product_filters');
}
public function categories()
{
return $this->belongsToMany('App\Filter', 'category_filter');
}
}
And categories which have many to many relationship with the filters
Category.php:
namespace App;
use App\Events\CategoryDelete;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $events = [
'deleting' => CategoryDelete::class
];
protected $table = 'categories';
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
public function parents()
{
$parentCategories = collect([]);
$parent = $this->parent;
while (!is_null($parent)) {
$parentCategories[] = $parent;
$parent = $parent->parent;
}
return $parentCategories->reverse();
}
public function products()
{
return $this->hasMany('App\Product');
}
public function filters()
{
return $this->belongsToMany('App\Filter', 'category_filter');
}
public function hasFilter($filter_id)
{
foreach ($this->filters as $filter) {
if ($filter->id == $filter_id) {
return true;
}
}
return false;
}
public function getFilterGroups()
{
$filterGroups = collect([]);
foreach ($this->filters as $filter) {
if (!$filterGroups->has($filter->group->id)) {
$filterGroups[$filter->group->id] = $filter->group;
}
}
return $filterGroups;
}
}
And in the category view I want to display the filters along with their filter group but when I try the following:
#foreach($category->filters as $filter)
{{ $filter->group->name }}
<br>
#endforeach
It throws the Trying to get property of non-object exception
Why can't I get the group of the filter?
I changed the group() method inside the Filter model to filterGroup and now when I call $filter->filterGroup it works fine. I dont know why, maybe group is some reserved word or something.

Laravel 5 - Dedicated Query string filtering on many-to-many Relationship

I Followed a tutorial with this source code:
https://github.com/laracasts/Dedicated-Query-String-Filtering/tree/master/app
If you have laracasts you can watch the video here
What i like to achieve is filter products based on their category.
When i filter on the product itself , it works fine
class ProductFilter extends QueryFilter
{
public function categorie($name)
{
return $this->builder->where('name' , $name);
}
}
But when i try to filter on the relationship it doens't work. (i get no errors either) . The error is located in this file , i think
class ProductFilter extends QueryFilter
{
public function categorie($name)
{
return $this->builder->categories()->where('name' , $name);
}
}
View
<form method="get" action="/producten/categorie" style="display:inline-block">
#foreach($roots as $root)
<li><button type="submit" name="categorie" value="{{$root->name}}" class="button-link">{{$root->name}}</button></li>
#endforeach
</form>
Route
Route::get('producten/categorie' , 'FrontProductController#index');
FrontProductController
public function index(ProductFilter $filters)
{
Product::filter($filters)->get();
}
QueryFilter class
abstract class QueryFilter
{
protected $request;
protected $builder;
public function __construct(Request $request)
{
$this->request = $request;
}
public function apply(Builder $builder)
{
$this->builder = $builder;
foreach ($this->filters() as $name => $value) {
if (! method_exists($this, $name)) {
continue;
}
if (strlen($value)) {
$this->$name($value);
} else {
$this->$name();
}
}
return $this->builder;
}
public function filters()
{
return $this->request->all();
}
}
Product Model
public function categories()
{
return $this->belongsToMany('App\Category')->withTimestamps();
}
public function scopeFilter($query, QueryFilter $filters)
{
return $filters->apply($query);
}
In the product filter i need to do the following for many-to-many relationships:
public function category($name)
{
return $this->builder->whereHas('categories', function ($query) use ($name) {
$query->where('name', $name);
});
}

Chain of hasMany or hasManyThrough through N models

How can I get all the submissions belongs to a particular user?
Trying this:
$user=User::find(1);
dd($user->submissions);
Throwing error:
Call to undefined method Illuminate\Database\Query\Builder::hasMany()
Will I have to loop through the models?
Here are the models:
class User extends Eloquent implements ConfideUserInterface, BillableInterface
{
public function categories()
{
return $this->hasMany('Category');
}
public function forms()
{
return $this->hasManyThrough('App\Models\Form', 'Category');
}
public function submissions()
{
return $this->hasMany('Category')->hasMany('Form')->hasMany('Submission');
}
}
class Category extends \Eloquent {
public function user()
{
return $this->belongsTo('User');
}
public function forms()
{
return $this->hasMany('App\Models\Form');
}
public function submissions()
{
return $this->hasManyThrough('Submission', 'App\Models\Form', 'id', 'form_id');
}
}
namespace App\Models;
class Form extends \Eloquent {
public function user()
{
return $this->category->user();
}
public function category()
{
return $this->belongsTo('Category');
}
public function submissions()
{
return $this->hasMany('Submission');
}
}
class Submission extends \Eloquent {
public function user()
{
return $this->form->category->user();
}
public function category()
{
return $this->form->category();
}
public function form()
{
return $this->belongsTo('App\Models\Form');
}
}
That doesn't really work that way with chaining relations...
What you can do, is this:
$submissions = Submission::whereHas('form.category.user', function($q){
$q->where('id', 1);
})->get();
Note that whereHas with nested relationships has only been added in the latest Laravel 4 release. Make sure to composer update.
If i'm not getting your question wrongly, you need to define relationship correctly.
According to laravel 4.2 you can use below kind of code to define relations:
class User extends \Eloquent implements ConfideUserInterface, BillableInterface{
//...
public function submissions(){
return $this->hasMany('Submission');
}
}
//...
class Submission extends \Eloquent {
public function user()
{
return $this->belongsTo('User');
}
//...
}
To further reading take a look at: http://laravel.com/docs/4.2/eloquent#relationships

Categories