Laravel paginate method not working with transform method - php

When I am using a transform method after calling paginate(20) it will return regular results, without paginating that is not supposed to be.
and when I remove transform() method paginate work as it supposes to be.
$users =
User::whereUser_role(1)
->join('user_types', 'users.user_type', '=', 'user_types.user_type_id')
->when(request('user-type'), function ($query) {
$query->where('user_types.name', '=', request('user-type'));
})
->addSelect(
'users.*',
'user_types.name as user_type',
)
->orderBy('users.created_at', 'desc')
->paginate(20)
->appends(request()->all())
->getCollection()
->transform(function ($user) {
$user->user_status = $user->status ? "Active" : "InActive";
$user->email_verified = $user->email_verified ? "Yes" : "No";
return $user;
});
$users->makeHidden(['user_role']);
return response()->json([
'message' => 'Success',
'status' => 200,
'requestLocation' => request()->path(),
'success' => true,
'data' => $users
], 200);

You can use getCollection method for the same. As per laravel API documentaion:
getCollection() : Get the paginator's underlying collection.
For e.g.
$users = User::whereStatus(1)
->paginate(20);
// iterates paginated items and applies a transformation
$users->getCollection()->transform(function ($user) {
$user->email_verified = $user->email_verified ? "Yes" : "No";
return $user;
})
Reference:Laravel API -> AbstractPaginator -> getCollection()

Related

Laravel query with subqueries and foreach

I wanna to show common users verified, not out of date and unbanned:
a) all songs
b) songs by title or text
c ) songs by tag
Two additions:
The user, which is an admin, can see unverified, banned and out of date songs and the user, which is an artist, can see unverified or banned songs too, but only his own ones.
It has been exhausting for several days, 'where in where, which is in loop...' is torture xD
Could you help my with scopeByUser funtion?
Song Model:
class Song extends Model
{
use HasFactory, Likable;
public function artist()
{
return $this->belongsTo(Artist::class, 'artist_id');
}
public function tags()
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
public function scopeByUser()
{
$user = current_user();
if ($user->hasRole('admin')) {
dd("x");
return $this;
} elseif (isset($user->artist)) {
return $this->where([
'isVerified' => true,
'isOutOfDate' => false,
'isBanned' => false
])->orWhere(function () use ($user) {
foreach ($user->artist as $artist) {
$this->where([
'artist_id', $artist->id,
'isOutOfDate' => false,
'isBanned' => false
]);
}
});
} else
return $this->where([
'isVerified' => true,
'isOutOfDate' => false,
'isBanned' => false
]);
}
}
SongController:
public function index(Request $request)
{
if (request('tag')) {
$songs = Song::query()
->whereHas('tags', function ($query) {
$tag = request('tag');
$query->where('name', $tag);
})
->ByUser()
->withLikes()
->get();
} elseif ($request) {
$search = $request->input('search');
$songs = Song::query()
->where('title', 'LIKE', "%{$search}%")->orWhere('text', 'LIKE', "%{$search}%")
->ByUser()
->withLikes()
->get();
} else {
$songs = Song::latest()
->ByUser()
->withLikes()
->get();
}
return view('welcome', compact('songs'));
}
One thing that I love about Laravel Eloquent is the when() method.
Laravel Collection #when()
I know the link is in the Collection, but it work with queries.
So, let review your queries and set it as one that you can make logical test and change it.
There is a non tested code in the only purpose of showing you what you could achieve.
$user = User::with('roles')->with('artists')->find(Auth::user()->id);
$songs = Song::when($request('tag'), function($query) use($request) {
$query->whereHas('tags', function($query2) use($request('tag')) {
$query2->where('name', $request('tag'));
});
})->when($request('search'), function($query) use($request) {
$query->where('title', 'LIKE', "%{$request('search)}%")->orWhere('text', 'LIKE', "%{$request('search)}%");
})->when(!isset($request('search')) && !isset($request('tags')), function($query) {
$query->latest();
})->when(true, function($query) use($user) {//setting when to true let you do more complexe logical test
if ($user->hasRole('admin')) {
//Nothing to do...
} else if (isset($user->artist)) {
$query->where([
'isVerified' => true,
'isOutOfDate' => false,
'isBanned' => false
])->orWhere(function ($query2) use ($user) {
foreach ($user->artist as $artist) {
$query2->where([//probably change this to orWhere. You can also get the array of artist id and use whereIn('id', $arrayOfId)->where(['isOutOfDate' => false, 'isBanned' => false]);
'artist_id', $artist->id,
'isOutOfDate' => false,
'isBanned' => false
]);
}
});
} else {
$query->where([
'isVerified' => true,
'isOutOfDate' => false,
'isBanned' => false
]);
}
})->ByUser()
->withLikes()
->get();
So, get back to what you really asked... using scope... I discourage using Scope when you are doing complexe logical test. WHY??? Simply because it look like your scope is retrieving the user and I'm assuming that this method make at least one request to DataBase... so, this lead to a N+1 problem.
Scope are good for simple task. One DB request in a scope will do the request for every single models. Avoid this.
If well used, when() method will help you to build complexe query that will result on a single DataBase query.

Multiline Eloquent query

I'm trying to filter my products based on selected filters and possibly a search term/word. My filters have a relationship with categories, which in their turn have a relation ship with my products. My code below only works (without the if statement checking for a search term/word) when everything is chained together, but when I try to break the query into multiple lines (which I've read is possible, right?) it returns an empty array.
Here's a my code:
// Create array from selected categories/filters
$filter_ids = explode(',', $request->get('cats'));
// Query for active products
$products = Product::where('active', '=', 1);
$products->with(['categories' => function($query) use ($filter_ids) {
// Query for active categories
$query->where('active', 1)->whereHas('filters', function ($query) use ($filter_ids) {
// Query for the selected filters from the request
$query->whereIn('id', $filter_ids);
});
}]);
// Check for search term/word
if ($request->get('q')) {
$q = $request->get('q') ? urldecode($request->get('q')) : null;
$products->where('title', 'LIKE', "%{$q}%");
}
// Limit to 10 items and get results
$products->limit(10)->get();
return response()->json([
'status' => 'success',
'response' => $products
], 200);
I think you could but don't need to query all products with title first, before adding the relationships. But whats wrong here is that you must store the result of get() in a variable before adding it to your json response body:
Try to do something like:
if ($request->get('q')) {
$q = $request->get('q') ? urldecode($request->get('q')) : null;
$products->where('title', 'LIKE', "%{$q}%");
}
$products->with(['categories' => function($query) use ($filter_ids) {
// Query for active categories
$query->where('active', 1)->whereHas('filters', function ($query) use ($filter_ids) {
// Query for the selected filters from the request
$query->whereIn('id', $filter_ids);
});
}]);
$response = $products->limit(10)->get();
return response()->json([
'status' => 'success',
'response' => $response
], 200);
Lukas' answer led me to do some more debugging and eventually solving my problem, though it was not the position of the if statement checking if there's a search term/word.
The problem lies in the following line:
$products->limit(10)->get();
I needed to store the retrieved results from the get(); method in another variable, in my case:
$response = $products->limit(10)->get();
I eventually ended up with the following working code:
// Create array from selected categories/filters
$filter_ids = explode(',', $request->get('cats'));
// Query for active products
$products = Product::where('active', '=', 1);
$products->with(['categories' => function($query) use ($filter_ids) {
// Query for active categories
$query->where('active', 1)->whereHas('filters', function ($query) use ($filter_ids) {
// Query for the selected filters from the request
$query->whereIn('id', $filter_ids);
});
}]);
// Check for search term/word
if ($request->get('q')) {
$q = $request->get('q') ? urldecode($request->get('q')) : null;
$products->where('title', 'LIKE', "%{$q}%");
}
// Limit to 10 items, get results and store in '$response'
$response = products->limit(10)->get();
return response()->json([
'status' => 'success',
'response' => $response
], 200);

Filtering in Laravel using regex

I'm trying to filter products based on query string. My goal is to get products from a collection if it's given, otherwise get every product. Could someone help me what's wrong with the following code?
$products = \App\Product::where([
'collection' => (request()->has('collection')) ? request('collection') : '[a-z]+',
'type' => (request()->has('type')) ? request('type') : '[a-z]+'
])->get();
PS.: I've also tried with 'regex:/[a-z]+', it's not working...
$products = \App\Product::where(['collection' => (request()->has('collection')) ? request('collection') : 'regex:/[a-z]+'])->get();
What you can do is use when eloquent clause, so your where clause for collections will be triggered only when the request('collection') exists, same logis applie to type as well.
$products = \App\Product::
when(request()->has('collection'), function ($q) {
return $q->where('collection', request('collection'));
});
->when(request()->has('type'), function ($q) {
return $q->where('type', request('type'));
})
->get();
Or another way if you have your request values assigned to a variable something like:
$collection = request('collection');
$type= request('type');
$products = \App\Product::
when(!empty($collection), function ($q) use ($collection) {
return $q->where('collection', $collection);
});
->when(!empty($type), function ($q) use ($type) {
return $q->where('type', $type);
})
->get();

Pass limit and offset values through URL using Lumen

I'm new on Resfully services and Lumen (Laravel micro-framework).
I want to pass the /books?limit=10&offset=5 parameters to the controller and set it on the json response and I can't figure out how.
web.php
$router->get('/books/', ['uses' => 'BooksController#index']);
BooksController.php
public function index()
{
$books = PartnersBooks::where('is_direct', '=', 1)
->with('direct')
->whereHas('direct', function ($query) {
$query->enable()
->select(['id', 'book_id', 'name', 'devices', 'flow', 'restrictions', 'countries', 'targeting']);
})
->offset(5) // This should have an default value until the user pass a value through url
->limit(30) // This should have an default value until the user pass a value through url
->orderBy('id', 'asc')
->get(['id', 'category', 'description']);
$status = !is_null($books) ? 200 : 204;
return response()->json($books, $status);
}
Could you please help me?
Thanks,
You could to use the Request object to do that:
use Illuminate\Http\Request;
public function index(Request $request)
{
$limit = $request->input('limit');
$offset = $request->input('offset');
$books = PartnersBooks::where('is_direct', '=', 1)
->with('direct')
->whereHas('direct', function ($query) {
$query->enable()
->select(['id', 'book_id', 'name', 'devices', 'flow', 'restrictions', 'countries', 'targeting']);
})
->offset($offset) // This should have an default value until the user pass a value through url
->limit($limit) // This should have an default value until the user pass a value through url
->orderBy('id', 'asc')
->get(['id', 'category', 'description']);
$status = !is_null($books) ? 200 : 204;
return response()->json($books, $status);
}

laravel query join with only latest record

I'm using Laravel 5.3 and trying to return a heist with it's product and only the latest order and with the latest price history. Both joins don't return anything but if I remove the $q->latest()->first(); and replace it with a simple orderBy() I get all results. My query is:
$data = $heist->with(['product'=> function($query) {
$query->with(['orders' => function($q) {
return $q->latest()->first();
}]);
$query->with(['price_history' => function($q) {
return $q->latest()->first();
}]);
}])->orderBy('completed_at', 'DESC')->orderBy('active', 'DESC')->get();
As discussed in the comments, I believe the simplest way of doing this is
$heists = $heist->with(['product'=> function($query) {
$query->with([
'orders' => function($q) {
return $q->orderBy('created_at', 'desc')->take(1)->get();
},
'price_history' => function($q) {
return $q->orderBy('created_at', 'desc')->take(1)->get();
}
]);
}])->orderBy('completed_at', 'desc')->orderBy('active', 'desc')->get();
Hope this helps :)
Calling first() is the same as calling take(1)->get()[0];
Which means limit the amount returned to 1 and return it. What you want is just the limit part. So if you change first() to take(1).
Update
$data = $heist->with([
'product'=> function($query) {
$query->with(
[
'orders' => function($q) {
$q->latest()->take(1);
},
'price_history' => function($q) {
$q->latest()->take(1);
}
]
);
}
])->orderBy('completed_at', 'DESC')->orderBy('active', 'DESC')->get();

Categories