How would I paginate these results in laravel? - laravel

I was reading this article to work out how to sort records in my database based on how many likes they have:
Laravel OrderBy relationship count
I came up with this which works:
$Book = Book::with('likes')->get()->sortByDesc(function($book_sort)
{
return $book_sort->likes->count();
});
Which is based upon this Book model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
public $timestamps = true;
protected $fillable = [
'title', 'author', 'category', 'featured', 'rating', 'description'
];
public function category()
{
return $this->hasOne('App\Cat', 'id', 'category_id');
}
public function likes()
{
return $this->belongsToMany('App\User', 'favourite_books')->withTimestamps();
}
public function total_likes()
{
return $this->likes()->count();
}
}
However now I am stuck on how I would paginate these results. Does anyone know?

Create manual pagination, try this:
$Book = Book::with('likes')->get()->sortByDesc(function($book_sort)
{
return $book_sort->likes->count();
});
$paginator = new Illuminate\Pagination\Paginator($Book, 10);
return view('pages.homepage', compact('paginator'))

sortBy() and sortByDesc() are working with collections only. orderBy() is working only with column name. Also, getting results, sorting and paginating them is a perfomance hit. It will also can eat all the memory.
So, the only solution I can see here is to use orderByRaw() or even raw query.

Related

Laravel - How to use eloquent ORM to populate a foreign key column when getting all results from a table?

I have setup my model as following:
class Items extends Model {
use HasFactory;
protected $table = 'item';
protected $primaryKey = 'id';
protected $connection = 'mysql';
public $timestamps = false;
protected $fillable = ['user_id', 'title', 'desc', 'start_datetime', 'due_datetime', 'priority', 'status'];
public function getManager() {
return $this->belongsTo(User::class, 'user_id');
}
public function getAssignees() {
return $this->belongsToMany(User::class);
}
}
I am getting all items using the controller method below, what I want to do is to populate the user_id field in each of the items using getManager() method I declared in my Item model. I know how to do this when getting only one item, but how to populate every record when getting all of them?
public function getall() {
try {
$items = Item::get();
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
I have tried this but no luck:
public function getall() {
try {
$items = Item::get();
$items = array_map(function ($el) {
return $el->manager = $el->getManager()->get();
}, $items);
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
There are a few things here that I have some concerns about. Your code may work, but you are also doing more than you need to and not using Laravel how it was meant to be used.
Model Name
Your model name is Items, but it should be singular, Item. This helps Laravel automate things so you have less work to do.
https://laravel.com/docs/8.x/eloquent#eloquent-model-conventions
class Item extends Model {
Database Settings
You've set the $table, $primaryKey, and $connection attributes, but these should be automatic. You can probably remove them.
protected $table = 'items'; // assuming your model name is Item, this would automatically be 'items'
protected $primaryKey = 'id'; // default is already 'id'
protected $connection = 'mysql'; // default is your main db, probably already 'mysql', unless if you have multiple db connections
Timestamps
I'm not sure why you'd want to turn timestamps off. You definitely can but I always find it helpful to know when something was created or last updated. Since Laravel handles the timestamps for you, I'd suggest leaving it on, but it's up to you.
https://laravel.com/docs/8.x/eloquent#timestamps
public $timestamps = false;
Manager Relationship
Your manager relationship is getManager but should just be manager. It will still work, but isn't how Laravel was meant to work. I would suggest changing it to manager(), and not specifying the column name. This would make the column name automatically manager_id, so you'd have to update that. Or you can keep the column name 'user_id'.
https://laravel.com/docs/8.x/eloquent-relationships#one-to-many-inverse
public function manager() {
return $this->belongsTo(User::class);
}
Assignees Relationship
Same as with the Manager relationship, you should change getAssignees() to assignees(). I'm assuming you already have a database migration set up for your 'item_user' table that Laravel will look for. If not, check the Laravel docs on how to set it up.
https://laravel.com/docs/8.x/eloquent-relationships#many-to-many
public function assignees() {
return $this->belongsToMany(User::class);
}
Retrieving Items
Finally, with the above changes, getting all Items should be easy. To load the relationships, use the $with method. This is called Eager Loading. Check the docs for more info.
https://laravel.com/docs/8.x/eloquent-relationships#eager-loading
$items = Item::with('manager','assignees')->get();
Returning Response Codes
You were returning your responses incorrectly. You do not need to set the response code 200, as this is the default. If you are going to set it to something else, put the code in the response() method, instead of the json() method.
https://laravel.com/docs/8.x/responses
return response()->json(['items' => $items]);
return response($err,400);
Now putting it all together, your Item model should look something like this:
class Item extends Model {
use HasFactory;
protected $fillable = ['manager_id', 'title', 'desc', 'start_datetime', 'due_datetime', 'priority', 'status'];
public function manager() {
return $this->belongsTo(User::class);
}
public function assignees() {
return $this->belongsToMany(User::class);
}
}
public function getall() {
try {
$items = Item::get()
->transform(function($el){
$el->manager = $el->getManager()->get();
);
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
Try the transform method on your results and it would work.
https://laravel.com/docs/8.x/collections#method-transform
the transform function would basically just iterate over the results and do whatever it is told to like a for loop but for collections.
Also, to make your query efficient avoid the use of loading the relation in the transform function and and use with function of laravel to make it efficient

Laravel Polymorphic Relationships with order by

Laravel version 7.2.5.
I am using Polymorphic Relationships to store the access logs (login, logout) for multi-role application.
The data storing part is working completely fine. Now, I need to show the list of records in desc format with pagination. But, It's loading the data in the asc format
SomeModel.php
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this->morphMany(LoginLog::class, 'employable');
}
public function show($token)
{
return self::where([
'token' => $token,
])->with([
'loginLog:employable_id,ip,created_at,updated_at'
])->first() ?? null;
}
}
I did find the solution. But, somehow it doesn't feel the appropriate way to solve this issue.
Here is the link Laravel order results by column on polymorphic table
Try this
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this
->morphMany(LoginLog::class, 'employable')
->latest();
}
}
I found another way to solve this issue...
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this->morphMany(LoginLog::class, 'employable');
}
public function show($token, $pagination = 0)
{
return self::where([
'token' => $token,
])->with([
'loginLog' => function ($query) use ($pagination) {
return $query->select([
'employable_id',
'ip',
'created_at',
'updated_at',
])
->skip($pagination)
->take(\Common::paginationLimit())
->orderBy('id', 'DESC');
}
])
->orderBy('id', 'DESC')
->first('id') ?? null;
}
}
Since I do not need the base table's parameters, therefore, I am fetching only id from it.
Also, with this way I am able to use the pagination too (I am using loadmore pagination).

Getting specific value when using eloquent from Laravel

I am using Laravel 5.2 and I need to get specific values from the database with a leftjoin. The code I am using is as follow:
public function commentList(Request $request)
{
$inputs = $request->all();
$commentList = Comment::select(
'projects_comments.id as comment_id',
'u.name as user_name',
'projects_comments.comment as comment',
'projects_comments.created_at as created_at'
);
$commentList->leftjoin('users AS u', 'projects_comments.user_id', '=', 'u.id');
if (!empty($inputs['project_ids'])) {
$commentList->where(function ($query) use ($inputs) {
foreach ($inputs['project_ids'] as $i) {
$query->orWhere('projects_comments.project_id', $i);
}
});
};
$data = $commentList->get();
return $data;
}
It works fine but I would like to know if there is a better way to do this using eloquent but I can't really understand how to write this for eloquent to work. I need to get all the comments from an array of project ids.
I have the following model for Comment:
class Comment extends Model
{
protected $table = 'projects_comments';
public $timestamps = true;
protected $guarded = ['id'];
public function project()
{
return $this->belongsTo('App\Project', 'project_id');
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
}
I assume what you want is to get Comments (with their users) that belongs to specific Projects provided by the user as an array of IDS
Comment::whereIn('project_id', $inputs['project_ids'])->with('user')->get();
And if you only want the id and name of the user associated with the comment, pass the fields to the with function like so
Comment::whereIn('project_id', $inputs['project_ids'])
->with('user:id,name')->get();

Update index algolia

I'm using Laravel 5.5 with Scout. I have an index in algolia using Documents and the users associated with theses Documents
class Documents extends Model
{
use Searchable;
public function toSearchableArray()
{
$data = $this->toArray();
// formatting relationship for algolia
$data['users'] = $this->types->toArray();
$data['document_type'] = $this->typeDocuments->name;
return $data;
}
protected $fillable = array('name', 'description', 'document_type');
public function types() {
return $this->belongsToMany('App\Users', 'document_rights', 'user_id', 'id');
}
public function typeDocuments() {
return $this->belongsTo('App\Document_type', 'document_type');
}
}
In my case, I will update one day the name of the Users, like this:
public function update(Request $request)
{
$user = Users::find(5);
$user->name = 'jean floriot';
$user->update();
}
But it never changes the User in the index of Algolia. Any ideas how to proceed ?
I believe you can use the searchable() method for that. After $this->update() you will get something like:
App\Documents::where('user_id', '=', $this->id)->searchable();
Please let me know if that worked or if I missed something.

How to create self referential relationship in laravel?

I am new to Laravel. I Just want to create a self referential model. For example, I want to create a product category in which the field parent_id as same as product category id. How is this possible?
Model Shown below
class Product_category extends Eloquent
{
protected $guarded = array();
public static $rules = array(
'name' => 'required',
'parent_id' => 'required'
);
function product_category()
{
return $this->belongsto('Product_category','parent_id');
}
}
It results Maximum function nesting level of '100' reached, aborting! Error
You can add a relation to the model and set the custom key for the relation field.
Update:
Try this construction
class Post extends Eloquent {
public function parent()
{
return $this->belongsTo('Post', 'parent_id');
}
public function children()
{
return $this->hasMany('Post', 'parent_id');
}
}
Old answer:
class Post extends Eloquent {
function posts(){
return $this->hasMany('Post', 'parent_id');
}
}
Your model is not at fault for producing the "maximum function nesting level of '100' reached" error. It's XDebug's configuration; increase your xdebug.max_nesting_level.
The following is from a 2015 post by #sitesense on laracasts.com:
This is not a bug in Laravel, Symfony or anything else. It only occurs when XDebug is installed.
It happens simply because 100 or more functions are called recursively. This is not a high figure as such and later versions of XDebug (>= 2.3.0) have raised this limit to 256. See here:
http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100
EDIT: In fact the latest Homestead provisioning script already sets the limit to 250. See line 122 here:
https://github.com/laravel/settler/blob/master/scripts/provision.sh#L122
So the addition of xdebug.max_nesting_level = 250 to php.ini should do it.
I've added a little more to the code based on your comments trying to access the parent!
class Person extends \Eloquent {
protected $fillable = [];
var $mom, $kids;
function __construct() {
if($this->dependency_id<>0) {
$this->mother->with('mother');
}
}
public function children() {
$children = $this->hasMany('Person','dependency_id');
foreach($children as $child) {
$child->mom = $this;
}
return $children;
}
public function mother() {
$mother = $this->belongsTo('Person','dependency_id');
if(isset($mother->kids)) {
$mother->kids->merge($mother);
}
return $mother;
}
}
Then you can access the parent from the child with eager loading, see more here: http://neonos.net/laravel-eloquent-model-parentchild-relationship-with-itself/
you can refer self, using $this
class Post extends Eloquent {
function posts(){
return $this->hasMany($this, 'parent_id');
}
}
Take a look at my answer here.
The key is this code below in Model.php
public function children()
{
return $this->hasMany(Structure::class, 'parent_id')->with('children');
}

Resources