Laravel many-to-many relationship OrderBy - mysql

I have 2 models in a Many to Many relationship. Let's say User and Role.
I want to sort my users based on the ASC/DESC of a field in Role.
My User & Role classes:
class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role','role_user');
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User','role_user');
}
I can sort the roles in each user but I cant sort the users
$query = User::with(array('roles'=> function ($query)
{
$query->select('role_name')->orderBy('role_name','asc');
}))->get();
I have also tried:
$query = User::with(roles)->orderBy('role_name','asc')->get();
But the error says column role_name does not exist.
Ideal result should look like this:
[
{
user_id:6
roles: [
"Admin",
"Baby"
]
},
{
user_id:2
roles: [
"Baby"
]
},
{
user_id:11
roles: [
"Baby",
"Cowboy"
]
}
]
I'd appreciate any help.

As user can have many roles i think you can concatenate role names and then order users by concatenated string. Try this:
User::selectRaw('group_concat(roles.name order by roles.name asc) as role_names, users.id')->
join('role_user','users.id','=','role_user.user_id')->
join('roles', 'roles.id','=','role_user.role_id')->
groupBy('user_id')->
orderBy('role_names','desc')->
get()

Please try the below modification in roles() function in User Class and then fetch it with User.
class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role','role_user')
->selectRaw('id,'role_name')
->orderby('role_name');
}
}
$query = User::with(roles)->get();
Hope this will be useful for you.

Related

How can I format the array on a specific format in my laravel controller function?

I'm currently creating a laravel vue spa, and just wondering on how can I get designation names with these designation id's with the same structure. This is the json of designation id's:
[
[
1,
5
],
[
1,
3
]
]
This is my getDesignations function in EmployeesController.php:
public function getDesignations($id) {
$employee_designation_ids = Employees::find($id)->pluck('designation_id')->toArray();
$designation_name = [];
foreach ($employee_designation_ids as $employee_designation_id) {
$designation = Designations::where('id', '=', $employee_designation_id);
//$designation_name[] = $designation;
}
return $employee_designation_ids;
}
If you want specifically that format, you can do it like this (with a lot of guesses in my part since you did not share your Tables structures)
public function getDesignations($id) {
$employee_designation_ids = Employees::find($id)->designation_id;
return [[$id, $employee_designation_ids]];
}
But why are you returning a double table for a single row result.
Thanks for all your help! I've managed to fix it with this method in my controller:
public function getDesignations(Request $request, $id) {
$employee_designation_ids = Employees::where('id', $id)->pluck('designation_id');
return Designations::whereIn('id', $employee_designation_ids[0])->pluck('name');
}

Using Objection JS How can I select specific columns with withGraphFetched

I have this question:
How can I indicate which columns I want get from database with the withGraphFetched method, I have a BelongsToOneRelation and I want exclude some columns, this is my model:
module.exports = class ProveedorModel extends Model {
...
static relationMappings = {
empresa: {
relation: Model.BelongsToOneRelation,
modelClass: EmpresaModel,
join: {
from: 'proveedor.empresa_id',
to: 'empresa.id'
}
}
};
...
}
and in my controller I have this:
const payload = await ProveedorModel.query().withGraphFetched('empresa');
but table empresa has to many columns which I won't, so how can I filter?
you can specify filter property for your relationship
class Person extends Model {
static relationMappings = {
pets: {
relation: Model.OneToManyRelation,
modelClass: Animal,
filter: query => query.select('id', 'ownerId', 'name'),
join: {
from: 'Person.id',
to: 'Animal.ownerId'
}
}
}
}
ref: https://github.com/Vincit/objection.js/issues/70#issuecomment-175143072
Just wondering why objection doesn't query only columns mapped in tableMetadata (https://vincit.github.io/objection.js/api/model/static-methods.html#static-tablemetadata) when used withGraphFetched like it does for withGraphJoined
Alternatively, you could map just the properties you want with parsedatabasejson
(https://vincit.github.io/objection.js/api/model/instance-methods.html#parsedatabasejson) but your SQL query will bring them all.

Laravel Eloquent eager loading multiple relationships to same model generates multiple queries

I am currently working on a project where translations are a core feature. Implementing this with Eloquent has been a great experience so far, but the problem unfolds when a model has multiple relations to one model.
Say you have a model called Post:
<?php
namespace App;
// posts (
// id
// title
// content
// )
class Post extends \Illuminate\Database\Eloquent\Model {
public function title() {
// posts.title -> translations.reference
return $this->hasMany('App\Translation', 'reference', 'title');
}
public function content() {
// posts.content -> translations.reference
return $this->hasMany('App\Translation', 'reference', 'content');
}
}
And a Translation model:
<?php
namespace App;
// translations (
// id
// reference
// language
// value
// )
class Translation extends \Illuminate\Database\Eloquent\Model {
public function language() {
return $this->hasOne('App\Language', 'id', 'language');
}
}
And finally a Language model:
<?php
namespace App;
// languages (
// id
// code
// name
// )
class Language extends \Illuminate\Database\Eloquent\Model { }
Then the following will successfully generate the expected output:
Post::with([
'title.language',
'content.language'
])->get();
[
{
"id":1,
"title":[
{
"id":68,
"reference":"5efd07083a002",
"language":{
"id":1,
"code":"en",
"name":"English"
},
"value":"Title in english"
}
],
"content":[
{
"id":70,
"reference":"5efd0708f1c82",
"language":{
"id":1,
"code":"en",
"name":"English"
},
"value":"Lorem in English"
}
]
}
]
THE PROBLEM
Is that Eloquent sent duplicate (or very similar) queries to the database, making me wonder if there is a better solution:
[
{
"query":"select * from `translations` where `translations`.`reference` in (?)",
"bindings":[
"5efd07083a002"
],
"time":186.14
},
{
"query":"select * from `languages` where `languages`.`id` in (?, ?)",
"bindings":[
1,
2
],
"time":170.69
},
{
"query":"select * from `translations` where `translations`.`reference` in (?)",
"bindings":[
"5efd0708f1c82"
],
"time":171.93
},
{
"query":"select * from `languages` where `languages`.`id` in (?, ?)",
"bindings":[
1,
2
],
"time":142.38
}
]
After doing some research I could not find any answers to this problem except "it's not possible" with no explanation.
What is the best solution for this problem? Is there something that I am missing or is this really impossible?
Thankful for any answers!

One to Many relationship, sort and filter children

I want to display all posts which like the user. OK. I can use this:
$user = User::where('slug', $user->slug)
->first();
dd($user->likes);
But it doesn't what I want to do. Which any post have to be accept by moderator (->where('accept', 1)) and orderign (->orderBy('created_at', 'desc')).
Who know how I can do that?
Currently I have 2 models. My relationships:
//Post
public function likes(){
return $this->hasMany('App\Like');
}
//Likes
public function post(){
return $this->belongsTo('App\Post');
}
//migrate of likes look like this
Schema::create('likes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id');
$table->integer('post_id');
$table->timestamps();
});
How I can solve my problem?
You could set up an M:N relationship between User and Post using Likes as pivot.
# User model
public function likes()
{
return $this->hasMany(Likes::class, 'user_id');
}
public function liked_posts()
{
return $this->belongsToMany(Post::class, 'likes', 'user_id', 'post_id');
}
# Likes model
public function post()
{
return $this->belongsTo(Post::class);
}
You could set it up like you have, User has many Likes belongs to Post.
// Using only likes and Likes's post relationship
$user = User::with(['likes.post' => function ($posts) {
$posts->where('accept', 1)->orderBy('created_at', 'desc');
}])->where('slug', $slug)->first();
Or you could use the M:N relationship.
// Using the liked_posts relationship
$user = User::with(['liked_posts' => function ($posts) {
$posts->where('accept', 1)->orderBy('created_at', 'desc');
}])->where('slug', $slug)->first();

Eloquent many to many relationship without middle table

I'm new to Eloquent and struggling mightily with the following.
In my database (mysql 5.7), there are 2 tables and structured as below.
article:
{
_id: 1,
title: "xxx",
content: "xxx",
tag_ids: [
4,
5
]
}
tag:
{
_id: 4,
tag: "tag1"
}
In ArticleModel, has a cast
protected $casts = [
"tags" => "array"
];
It is possible to make a many to many relationship without a middle table?
Any help would be greatly appreciated!
I created a package with JSON relationships: https://github.com/staudenmeir/eloquent-json-relations
You can create a many-to-many relationship like this:
class Article extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'tag_ids' => 'array'
];
public function tags()
{
return $this->belongsToJson(Tag::class, 'tag_ids');
}
}
class Tag extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function articles()
{
return $this->hasManyJson(Article::class, 'tag_ids');
}
}