Query Scope for this eloquent method? - php

I have create a filter method in my project where I filtered the data using this method but now I want to refactor the code using queryScope method in laravel can anyone suggest me how to refactor this code.
This code is working fine.
This is my controller index method
public function index(Request $request)
{
$status = Ticket_status::pluck('name');
$tickets = Ticket::with('users','ticketStatus','ticketType','tbl_contacts')
->where('user_id','=',Auth::user()->id)
->latest();
if (request('Open')) {
$tickets = $tickets->where('status_id',1)->get();
} elseif (request('Pending')) {
$tickets = $tickets->where('status_id',2)->get();
} elseif (request('Close')) {
$tickets = $tickets->where('status_id',3)->get();
} else {
$tickets = $tickets->get();
}
return view('ticketing.user.index',compact('tickets','status'));
}
and this is my blade file.. In this all code is running good but I want to some refactor
<div class="col-md-8">
<a
href="{{route('tickets.index')}}"
class="btn btn-sm btn-outline-secondary mr-1">
All
</a>
#foreach ($status as $status_name)
<a
href="/tickets?{{Str::lower($status_name)}}={{ Str::lower($status_name) }}"
class="btn btn-sm btn-outline-secondary mr-1">
{{$status_name}}
</a>
#endforeach
</div>
And this is my model.
<?php
namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Ticket extends Model
{
use SoftDeletes;
//Table Name
protected $table = 'tickets';
//Primary key
public $primaryKey = 'id';
protected $fillable = [
'ticket_number',
'name',
'description',
'contact_id',
'product_id',
'status_id',
'type_id',
'priority',
'user_id',
'ticket_image',
'start_date',
];
protected $casts = [
'start_date' => 'datetime',
];
protected $dates = [
'start_date',
'deleted_at',
];
protected $filepath = '/storage/';
public function getRouteKeyName()
{
return 'ticket_number';
}
public function setStartDateAttribute($date)
{
$this->attributes['start_date'] = Carbon::parse($date)->format('Y-m-d H:i:s');
}
public function getTicketImageAttribute($value)
{
return asset($value ? $this->filepath.$value: 'uploads/default/products.jpg');
}
public function ticketType() {
return $this->belongsTo( 'App\Ticket_type', 'type_id' );
}
public function ticketStatus() {
return $this->belongsTo( 'App\Ticket_status', 'status_id' );
}
public function tbl_contacts() {
return $this->belongsTo('App\Tbl_contacts', 'contact_id');
}
public function tbl_products() {
return $this->belongsTo('App\Tbl_products', 'product_id');
}
public function users() {
return $this->belongsTo('App\User', 'user_id');
}
public function getPriorityAttribute($value) {
if ($value == 1) {
// return "<span class='dot dot-sm dot-success'></span> Low";
return $value;
} elseif($value == 2) {
// return "<small class='dot dot-sm dot-warning'></small> Medium";
return $value;
} else {
// return "<span class='dot dot-sm dot-danger'></span> High";
return $value;
}
}
/**
* Get all of the Ticket's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
// public function scopeFilter($query, $filters) {
// if ($stauts = $filters['open']) {
// $query->where('status_id','=',$stauts);
// } elseif ($stauts = $filters['pending']) {
// $query->where('status_id','=',$stauts);
// } elseif ($stauts = $filters['close']) {
// $query->where('status_id','=',$stauts);
// }
// }
}

You can define local scopes on the model to refactor the query. Here are few:
class Ticket extends Model
{
use SoftDeletes;
public function scopeByAuthUser($query)
{
return $query->where('user_id','=', \Auth::user()->id);
}
public function scopeOpen($query)
{
return $query->where('status_id', 1);
}
public function scopePending($query)
{
return $query->where('status_id', 2);
}
public function scopeClose($query)
{
return $query->where('status_id', 2);
}
}
Here's how you can refactor your condition:
// for the first query
$tickets = Ticket::with('users','ticketStatus','ticketType','tbl_contacts')
->byAuthUser()
->latest();
if(request('Open') || request('Pending') || request('Close')) {
$scope = strtolower(request('Open') ?? request('Pending') ?? request('Close'));
$tickets = $tickets->{$scope}()->get();
} else {
$tickets = $tickets->get();
}

Related

Laravel Query Search Function

This is my controller:
public function index($mid,$payload){
$search = $payload['search'];
$users = DB::select('SELECT a.id, a.alternate_id, a.setujuterma, a.mykad, a.nama, a.email, a.notel, a.etunai,
b.ranktitle, c.ranktitle AS appointed_rank, d.nama as hirarki, e.alternate_id as placement,
e.nama as leadername, a.akses, a.suspendreason, a.regstamp,
a.matagajet, f.display as hirarkidisplay, IF(a.mykadverify = "3","1","0") as mykadverifydecode
FROM pengguna as a
LEFT JOIN penggunarank b ON a.effective_rank = b.id
LEFT JOIN penggunarank c ON a.appointed_rank = c.id
LEFT JOIN hirarki d ON a.userrank = d.id
LEFT JOIN pengguna e ON a.placement = e.id
LEFT JOIN hirarkimid f ON a.userrank = f.hirarki AND a.mid = f.mid
WHERE a.mid ='. $mid .' AND a.akses != -1'
);
$sortUser = collect($users)->sortByDesc('alternate_id')->toArray();
$collection = collect($sortUser);
$count = count($users);
// SEARCH BOX
if ($search) {
$collection->where(function ($q) use ($search) {
$q->where("alternate_id","LIKE","%{$search}%")
->orWhere("nama","LIKE","%{$search}%")
->orWhere("mykad","LIKE","%{$search}%")
->orWhere("notel","LIKE","%{$search}%")
->orWhere("email","LIKE","%{$search}%");
});
}
return [
$user,
$count
];
}
So,
$users return an array.
$collection return collection
for the search box, if I use $users, I get error
"Call to a member function where() on Array"
and if I use $collection, I get
message: "explode() expects parameter 2 to be string, object given", exception: "ErrorException",…}
Any help would be greatly appreciated. Thanks.
public function search(Request $payload){
$search = $payload['search'];
if($search == "")
{
$users = Payee::whereNotNull('payee_name')->take(10)->get();
}
else
{
$users = Payee::whereNotNull('payee_name')
->where(function ($q) use ($search) {
$q->where("payee_name","LIKE","%$search%")
->orWhere("payee_nick_name","LIKE","%$search%");
})->take(10)->get();
}
return [
$users,
];
}
I found an answer to my question. All I need is to change the query into eloquent model class. There is no other way if I want to use the where() function for my search. First I create model User.php:
<?php
namespace App;
use App\WithdrawEcash;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Propaganistas\LaravelPhone\PhoneNumber;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable, HasFactory;
protected $table = 'pengguna';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $guarded = ['id'];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
// user registered by
public function userRegby()
{
return $this->belongsTo(User::class, 'regby');
}
// user leader
public function userPlacement()
{
return $this->belongsTo(User::class, 'placement');
}
public function penggunaRank()
{
return $this->belongsTo(PenggunaRank::class, 'effective_rank');
}
public function appointedRankUser()
{
return $this->belongsTo(PenggunaRank::class, 'appointed_rank');
}
public function penyatabonus()
{
return $this->belongsTo(User::class, 'id', 'pengguna');
}
//Hirarkimid userrank
public function userHirarki()
{
return $this->belongsTo(Hirarkimid::class, 'userrank', 'hirarki');
}
public function userhirarchy()
{
return $this->belongsTo(Hierarchy::class, 'userrank')->select('id', 'nama');
}
public function systemHirarki()
{
return $this->belongsTo(Hierarchy::class, 'userrank');
}
// user order
public function userOrders()
{
return $this->hasMany(Order::class, 'pengguna');
}
// user order for registration report
public function userOrder()
{
return $this->hasOne(Order::class, 'pengguna');
}
public function hierarchy()
{
$hirarki = $this->belongsTo(Hirarkimid::class, 'userrank', 'hirarki')
->select('hirarki', 'display', 'shownilaibelian', 'show_harga_ketika_pesanan')
->where('mid', auth()->user()->mid);
if ($hirarki) {
return $hirarki;
} else {
return $this->belongsTo(Hierarchy::class, 'userrank')->select('id', 'nama');
}
}
public function myCartLists()
{
return $this->hasMany(AddToCart::class, 'user_id');
}
public function bonusStatement()
{
return $this->hasMany(PenyataBonus::class, 'pengguna');
}
public function currentBonusStatement()
{
return $this->hasMany(PenyataBulanSemasa::class, 'pengguna');
}
public function withdrawEcash()
{
return $this->hasMany(WithdrawEcash::class, 'pengguna');
}
public function fileupload()
{
return $this->morphOne(FileUpload::class, 'file_upload');
}
public function fileuploads()
{
return $this->morphMany(FileUpload::class, 'file_upload');
}
public function voucherdetail()
{
return $this->hasMany(Voucherdetail::class, 'pengguna');
}
public function countryCode()
{
return $this->hasOne(Negara::class, 'nama', 'negara')->value('kod');
}
public function setNotelAttribute($value)
{
if (!is_null($value)) {
$country_code = $this->countryCode() != '' ? $this->countryCode() : 'MY';
$this->attributes['notel'] = PhoneNumber::make($value, $country_code)
->formatForMobileDialingInCountry($country_code);
} else
$this->attributes['notel'] = $value;
}
public function setNotelcsAttribute($value)
{
if (!is_null($value)) {
$country_code = $this->countryCode() != '' ? $this->countryCode() : 'MY';
$this->attributes['notelcs'] = PhoneNumber::make($value, $country_code)
->formatForMobileDialingInCountry($country_code);
} else
$this->attributes['notelcs'] = $value;
}
}
And in my controller I simply call the user model:
$user = User::query()->select('id', 'alternate_id', 'setujuterma', 'mykad', 'nama', 'email', 'notel', 'etunai',
'effective_rank','appointed_rank', 'akses', 'suspendreason', 'regstamp',
'matagajet', 'userrank','mykadverify','placement')
->with([
'penggunaRank' => function($q) use ($mid){
$q->select('id','ranktitle')->where('mid',$mid);
},
'appointedRankUser' => function($q) use ($mid){
$q->select('id','ranktitle')->where('mid',$mid);
},
'systemHirarki'=> function($q){
$q->select('id', 'nama');
},
'userHirarki' => function($q) use ($mid){
$q->select('hirarki','display')->where('mid',$mid);
},
'userPlacement' => function($q){
$q->select('id','alternate_id','nama');
}
])
->where('mid',$mid)
->where('akses','!=',-1);
if ($search) {
$user->where(function($q) use ($search){
$q->where("alternate_id","LIKE","%{$search}%")
->orWhere("nama","LIKE","%{$search}%")
->orWhere("mykad","LIKE","%{$search}%")
->orWhere("notel","LIKE","%{$search}%")
->orWhere("email","LIKE","%{$search}%");
});
}
return $user->orderBy('alternate_id','desc')
Hope everyone can get benefits from this. Thank you.

Call to a member function toArray() on integer

User.php
public function role()
{
return $this->belongsToMany('App\Models\Role','user_role','user_id','role_id');
}
//проверка принадлежит ли пользователь к какой либо роли
public function isEmloyee(){
$role=$this->role->toArray();
return !empty($role);
}
//проверка имеетли пользователь определению роли
public function hasRole($check){
return in_array($check,array_pluck($this->role->toArray(),'name'));
}
//получение идентификатора роли
private function getIdinArray($array,$term){
foreach ($array as $key => $value){
if ($value == $term){
return $key +1;
}
return false;
}
}
//добавление роли пользователя
public function makeEmployee($title){
$assiqned_role = array();
$role = array_pluck(Role::all()->toArray(),'name');
switch ($title){
case 'admin':
$assiqned_role[] = $this->getIdinArray($role,'admin');
case 'client':
$assiqned_role[] = $this->getIdinArray($role,'client');
break;
default:
$assiqned_roles[] = false;
}
$this->role()->attach($assiqned_role);
}
Role.php
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User','user_role','role_id');
}
}
OwnerMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
class OwnerMiddleware
{
public function handle($request, Closure $next,$role)
{
if(!$request->user()->hasRole($role)) {
return redirect('/');
}
return $next($request);
}
}
You have role column in database. It preserves access to your role relation collection. You should delete it or rename role() relation for example to roles(). Moreover, belongsToMany implies that user can have many roles.
In addition, I want to note that the collection has its own methods in_array => contains, array_pluck => pluck. You could optimize your code like that:
public function roles()
{
return $this->belongsToMany(Role::class, 'user_role');
}
public function isEmloyee(){
return $this->roles->isNotEmpty();
}
public function hasRole($name){
return $this->roles->pluck('name')->contains($name);
}
public function makeEmployee($name){
$role = Role::where('name', $name)->first();
if($role){
$this->role()->attach($role->id);
}
}

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);
});
}

Sort by selectRaw relationship in Laravel (calculated count, sum ...)

I have a Question model which have morphMany Answer. Answer have also morphMany Answer and Rating
I added relationship to calculate and load Answer count and Answer rating sum :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use App\Facades\AuthManager;
class Answer extends Model {
protected $with = [
'user',
'cards',
'answers.user',
'answersCountRelation',
'ratingSumRelation'
];
protected $fillable = [
'text',
'user_id'
];
public function answerable() {
return $this->morphTo();
}
public function user() {
return $this->belongsTo(User::class);
}
public function answers() {
return $this->morphMany(Answer::class, 'answerable');
}
public function reports() {
return $this->morphMany(Report::class, 'reportable');
}
public function cards() {
return $this->hasMany(Card::class);
}
public function ratings() {
return $this->hasMany(Rating::class);
}
public function hasReported(User $user) {
return ($user != null && $this->reports()->where([
'user_id' => $user->id
])->first() != null);
}
public function hasRated(User $user) {
return ($user != null && $this->ratings()->where([
'user_id' => $user->id
])->first() != null);
}
public function answersCountRelation() {
return $this->answers()->selectRaw('answerable_id, count(*) as count')->groupBy('answerable_id');
}
public function getAnswersCountAttribute() {
return ($this->answersCountRelation->first() ? $this->answersCountRelation->first()->count : 0);
}
public function ratingSumRelation() {
return $this->ratings()->selectRaw('answer_id, SUM(power) as sum')->groupBy('answer_id');
}
public function getRatingSumAttribute() {
return ($this->ratingSumRelation->first() ? $this->ratingSumRelation->first()->sum : 0);
}
}
Now I wonder how can I load Question with Answer already sorted by answer count, rating, created_at etc
Is it possible to do it using the already eager loaded answersCountRelation or ratingSumRelation ?
Or do I have to do another request in Question->with(['answers' => function ($q) ...
Thx !

Categories