Dingo API pagination with illuminate builder not eloquent - php

I am in the process of starting to learn Laravel 5.3 and Dingo API to build a basic REST API. I am trying to return the paginated JSON representation of a report query result. I can get pagination to work using Eloquent models, however not when using the raw Illuminate QueryBuilder for DB access. Here is what I've been trying:
public function getWentCommission(Request $request, $start, $end)
{
$filters = $request->input('filter');
$query = DB::table('commissions')
->selectRaw("fields")
->leftJoin('users', 'users.id', '=', 'commissions.customerid')
->leftJoin('customers', 'customers.id', '=', 'commissions.customerid')
->whereBetween('datestart', [$start, $end])
->orWhereBetween('dateend', [$start, $end])
->orWhereNull('dateend')
->where('void', '=', 0)
->orderBy('startdate');
// Filter by users?
if($filters && array_key_exists('users', $filters))
{
$rawids = array_map('intval', array_unique(array_filter(explode(',', $filters['users']), 'is_numeric' )));
$query = $query->whereIn('users.id', $rawids);
}
return $this->response->paginator(new IlluminatePaginatorAdapter($query->paginate(25)), new ObjectTransformer);
}
However, this only nets me the following error which I have yet to figure out how to resolve. Any advice would be greatly appreciated. I'm sure I'm missing something obvious here.
Type error: Argument 1 passed to League\Fractal\Pagination\IlluminatePaginatorAdapter::__construct() must implement interface Illuminate\Contracts\Pagination\LengthAwarePaginator, instance of Illuminate\Support\Collection given

Related

Can multiple queries be applied to a model in Laravel?

I'm currently trying to get data from the DB with a Laravel controller through a model Maintain. Is there a way to query the DB based on the request data made by an axios post from the front end, this includes both variables sub and zone, I have the ability to get the data from using sub OR zone but not both, is there a way to construct a multi where query if $request contains the necessary variables and a standard request if they are null or ""?
public function all(Request $request){
$query = [];
if($request->sub != ""){
array_push($query, ['subsystem', '=', $request->sub]);
}
if($request->zone != ""){
array_push($query, ['zone', '=', $request->zone]);
}
if(count($query) > 0){
return Maintain::all()->where($query);
}
else{
return Maintain::all();
}
}
Currently This returns with an error ErrorException: array_key_exists(): The first argument should be either a string or an integer in file but I've been using the Laravel reference and it doesn't seem to be working. I used Postman to get the readout of $query and it contains the following:
[
['sub', '=', 'Subsystem 1'],
['zone', '=', 'Zone 1']
]
Any help would be appreciated.
Try like this
public function all(Request $request){
$result = Maintain::when($request->zone, function ($q) use($request){
$q->where('zone', $request->zone);
})
->when($request->sub, function ($qw) use($request){
$qw->where('subsystem', $request->sub);
})
->get();
return($result);
}
when() method look like if-else
Edited: Let we know if you get an error: Happy coding

How to compare related count with own column in Laravel Eloquent?

Assume we have an agents table with a quota column and a many-to-many relationship to tickets. With Laravel Eloquent ORM, how can I select only agents having less or equal number of 'tickets' than their 'quota'?
Eager-loading objects must be avoided.
class Agent extends Model {
public function tickets()
{
return $this->belongsToMany(Ticket::class, 'agent_tickets')->using(AgentTicket::class);
}
public function scopeQuotaReached($query)
{
// Does not work. withCount is an aggregate.
return $query->withCount('tickets')->where('tickets_count', '<=', 'quota');
// Does not work. Tries to compare against the string "quota".
return $query->has('tickets', '<=', 'quota');
}
}
Is there a more eloquent (pun intended) way to solve this than using a DB::raw() query with joining and grouping and counting manually?
EDIT
Works:
$query->withCount('tickets')->having('tickets_count', '<=', DB::raw('quota'))->get();
Works:
$query->withCount('tickets')->having('tickets_count', '<=', DB::raw('quota'))->exists();
Breaks: (throws)
$query->withCount('tickets')->having('tickets_count', '<=', DB::raw('quota'))->count();
RELATED
https://github.com/laravel/framework/issues/14492
Issue is closed, links to #9307, I have posted there. Will follow up.
Derived columns like tickets_count can only be accessed in the HAVING clause.
Since there is no havingColumn() method, you'll have to use a raw expression:
$query->withCount('tickets')->having('tickets_count', '<=', DB::raw('quota'));
At a database level I don't know how to achieve this, but you could do it at a Collection level.
// Get users
$agents = Agent::withCount('tickets')->get();
// filter
$good_agents = $agents->filter(function ($agent, $key) {
return $agent->tickets_count >= $agent->quota;
})
->all();
Of course you can inline it:
$good_agents = Agent
::withCount('tickets')
->get()
->filter(function ($agent, $key) {
return $agent->tickets_count >= $agent->quota;
})
->all();

Laravel - Using a nested relationship with the query builder

I am working on an API but its starting to get a bit slow now that the data is increasing. I am moving some of the queries so that they use the DB query builder.
I have my last one which has a nested query:
$artists = Artist::with('performances', 'performances.stage')->get();
I have got so far:
$artists = \DB::table('artists')
->leftJoin('performances', 'artists.id', '=', 'performances.artist_id')
->get();
But now need to do the second relationship which in the Performance model is:
public function stage()
{
return $this->hasOne('App\Models\Stage', 'id', 'stage_id');
}
Any help on how I do this?
yes you can use eloquent relationship with query builder like this
$artists = Artist::join('performances', 'artists.id', '=', 'performances.artist_id')
->all();
foreach($artists as $artist){
$data = $artist->stage()->first();
}
It is very well covered in the official documentation, please, refer to this section of Documentation
I think that you want to achieve something like this:
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
And also, please, read carefully this section

Adding parameters to the relation in laravel

I have a query the uses many ->with() in Laravel like so:
$campaign = $this->campaign
->with('tracks.flights.asset')->take(4)
->with('tracks.group')
->with('tracks.media')
->with('tracks.flights.comments')
->orderBy('created_at', 'desc')->find($id);
and in the Track model i have this method:
public function flights()
{
return $this->hasMany('Flight\Flight')->orderBy('start_date');
}
What I want to achieve is having the same result as now but i want to put date constrains on the flight level, for example something like this:
public function flights($start_date, $end_date) //dynamic dates
{
return $this->hasMany('Flight\Flight')->whereRaw('start_date > $start_date and end_date < $end_date')->orderBy('start_date');
}
How can I achieve this kind of result? I am fairly new to Laravel.
Thanks in advance.
Chain a whereHas onto your query.
$campaign = $this->campaign
->with('tracks.flights.asset')->take(4)
->with('tracks.group')
->with('tracks.media')
->with('tracks.flights.comments')
->whereHas('tracks.flights', function($q) use ($start_date, $end_date) {
$q->where('start_date', '>', $start_date)->where('end_date', '<', $end_date)->orderBy('start_date');
})
->orderBy('created_at', 'desc')->find($id);
Keep in mind the orderBy will only order the results returned within the flights relation, not the campaigns themselves.
This is the solution i came up with after reading Eloquent ORM in Laravel
$campaign = $this->campaign
->with(array('tracks.flights' => function($query)
{
$query->whereRaw('flights.start_date > "2016-01-09 00:00" AND flights.end_date < "2016-01-16 00:00"')
->with('asset')->take(4)
->with('comments');
}
))
->with('tracks.group')
->with('tracks.media')
->orderBy('created_at', 'desc')->find($id);

laravel using query builder without chaining

I have a query builder that works:
$article = Page::where('slug', '=', $slug)
->where('hide', '=', $hidden)
->first();
But I want to only add the second where statement if hidden is equal to 1. I've tried the code below which shows the logic of what I'm trying to do, but it doesn't work.
$article = Page::where('slug', '=', $slug);
if ($hidden == 1) {
$article->where('hide', '=', 1);
}
$article->first();
I'm using Laravel 4, but I think the question still stands with Laravel 3.
Yeah there's a little "gotcha" with Eloquent and the query builder. Try the code below ;)
$query = Page::where('slug', '=', $slug);
if ($hidden == 1) {
$query = $query->where('hide', '=', 1);
}
$article = $query->first();
Note the assigning of $query within the conditional. This is becuase the first where (statically called) returns a different object to the query object within the conditional. One way to get around this, I believe due to a recent commit, is like so:
$query = Page::where('slug', '=', $slug)->query();
This will return the query object and you can do what you want as per normal (Instead of re-assigning $query).
Hope that helps.

Categories