Laravel Polymorphic Relationships with order by - laravel

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).

Related

How to insert into two table using laravel 8? This is my code

This is my code
I have a table inventory_transactions(id, part_name, part_number) and transaction_logs(id, description, inventory_transaction_id(FOREIGN KEY)). I want to insert the value of id to inventory_transaction_id.
What should I do?
namespace App\Http\Controllers;
use App\Models\Inventory_transaction;
use App\Models\Transaction_log;
use Illuminate\Http\Request;
public function store(Request $request){
$inventory_transaction = $request->validate($this->validation());
$value = Inventory_transaction::create($inventory_transaction);
return $this->respondWithMessage('Transaction added');
}
private function validation()
{
return [
'part_name' => ['required'],
'part_number' => ['required'],
];
}
Assuming that you have relationships defined as
class Inventory_transaction extends Model
{
public function transaction_logs()
{
return $this->hasMany(Transaction_log::class);
}
}
you can use the relation to insert related record
public function store(Request $request)
{
$validated = $request->validate($this->validation());
$transaction = Inventory_transaction::create($validated);
$transaction->transaction_logs()->create(['description' => 'Transaction Added.']);
return $this->respondWithMessage('Transaction added');
}
Having said that you should seriously spend some time going through Laravel docs, it will enable you to understand the basics very well - Laravel has one of the best documentation with lots of code examples for easy understanding of basic
Laravel relations: https://laravel.com/docs/8.x/eloquent-relationships#introduction

Laravel one to one (polymorphic) not working only on specific model

I am facing a super weird issue.
I have 3 tables and their equivalent model:
App\User
public function company() {
return $this->morphTo();
}
App\Supplier
public function user() {
return $this->morphOne(User::class, 'company');
}
App\Company
public function users() {
return $this->morphMany(User::class, 'company');
}
For some reason the relationship on App\Supplier does not work, but all the other works normal, I can even get the supplier if I have the user:
$supplier = \App\Supplier::find(1);
$company = \App\Company::find(2);
$supplieruser = \App\User::find(1);
$supplier->user //THIS RETURN NULL
$company->users //return collection of users normally
$suppplieruser->company //returns an instance of supplier model
I have tried, changing the name of the relationship and nothing.
Any idea?
In case someone find this in the future... the relationship was not working because I had in my AppServiceProvider the following:
Relation::morphMap([
.....
'supplier_to_supplier' => 'App\Supplier',
.....
]);
It seems that registering it here will affect all the morphs from that model.

Laravel - one-to-one relation through pivot table with eager load

I have this relationship
A Movement can have multiples steps
A Step can belongs to multiples Movements
So a had to create a pivot table and a belongsToMany relationship, but my pivot table have some extras columns, like finished and order
I want to have two relationships, one to get all steps from a movement and another one to get the current step from the movement (the last finished step)
I know how to get all steps
public function steps()
{
return $this->belongsToMany(MovementStep::class, 'movement_movement_steps')
->withPivot('order', 'finished')
->orderBy('pivot_order');
}
But how about the current step? I need this kind of relationship, but returning only one record and be able to eager load it cause I'm passing it to vue.js
public function current_step()
{
return $this->belongsToMany(MovementStep::class, 'movement_movement_steps')
->withPivot('order', 'finished')
->where('finished', true)
->orderBy('pivot_order', 'desc');
}
Notice, I'd like to do that without extras packages
alternative solution, but with extra package: Laravel hasOne through a pivot table (not the answer marked as correct, the answer from #cbaconnier)
A different approach from the answer provided by #mrhn is to create a custom relationship. Brent from Spatie did an excellent article about it
Although my answer will do the exact same queries than the one provided by staudenmeir's package it makes me realized that either you use the package, this answer or #mrhn answer, you may avoid the n+1 queries but you may still ends up will a large amount of hydrated models.
In this scenario, I don't think it's possible to avoid one or the other approach. The cache could be an answer though.
Since I'm not entirely sure about your schema, I will provide my solution using the users-photos example from my previous answer.
User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function photos()
{
return $this->belongsToMany(Photo::class);
}
public function latestPhoto()
{
return new \App\Relations\LatestPhotoRelation($this);
}
}
LastestPhotoRelation.php
<?php
namespace App\Relations;
use App\Models\User;
use App\Models\Photo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;
class LatestPhotoRelation extends Relation
{
/** #var Photo|Builder */
protected $query;
/** #var User */
protected $user;
public function __construct(User $user)
{
parent::__construct(Photo::query(), $user);
}
/**
* #inheritDoc
*/
public function addConstraints()
{
$this->query
->join(
'user_photo',
'user_photo.photo_id',
'=',
'photos.id'
)->latest();
// if you have an ambiguous column name error you can use
// `->latest('movement_movement_steps.created_at');`
}
/**
* #inheritDoc
*/
public function addEagerConstraints(array $users)
{
$this->query
->whereIn(
'user_photo.user_id',
collect($users)->pluck('id')
);
}
/**
* #inheritDoc
*/
public function initRelation(array $users, $relation)
{
foreach ($users as $user) {
$user->setRelation(
$relation,
null
);
}
return $users;
}
/**
* #inheritDoc
*/
public function match(array $users, Collection $photos, $relation)
{
if ($photos->isEmpty()) {
return $users;
}
foreach ($users as $user) {
$user->setRelation(
$relation,
$photos->filter(function (Photo $photo) use ($user) {
return $photo->user_id === $user->id; // `user_id` came with the `join` on `user_photo`
})->first() // Photos are already DESC ordered from the query
);
}
return $users;
}
/**
* #inheritDoc
*/
public function getResults()
{
return $this->query->get();
}
}
Usage
$users = \App\Models\User::with('latestPhoto')->limit(5)->get();
The main difference from Brent's article, is that instead of using a Collection we are returning the latest Photo Model.
Laravel has a way to create getters and setters that act similar to columns in the database. These can perfectly solve your problem and you can append them to your serialization.
So instead your current_step is gonna be an accessor (getter). The syntax is getCurrentStepAttribute() for the function which will make it accessible on the current_step property. To avoid N + 1, eager load the steps when you retrieve the model(s) with the with('steps') method. Which is better than running it as a query, as it will execute N times always.
public function getCurrentStepAttribute() {
return $this->steps
->where('finished', true)
->sortByDesc('pivot_order')
->first();
}
Now you can use the append property on the Movement.php class, to include your Eloquent accessor.
protected $appends = ['current_step'];

How would I paginate these results in 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.

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