Beauty name instead id - php

I have a two table:
Users:
id,
name,
role_id
Roles:
id,
name
And i will response to requset like this:
user_id, user_name, role_name (not the role_id)
My controller for route function:
public function getUserList(): JsonResponse {
$users = User::All()->toArray();
return response()->json($users);
}
How include roles.name field in the response ?

Create a relationship on your User model.
class User extends Authenticable
{
public function role()
{
return $this->belongsTo(Role::class);
}
}
Include the relationship using with().
$users = User::with('role')->get();
This will transform it into the following structure, when the models are transformed to JSON.
[
{
"name": "Martin",
"email": "me#email.com",
"role": {
"name": "admin"
}
}
]
Working with Laravel, i will call it an anti pattern, transforming objects or data structures to arrays. In between classes, keep em objects for flexibility. In controllers Laravel, is smart enough to automatically transform your data. So the following code, is enough for a JSON route.
public function getUserList()
{
return User::with('role')->get();
}

public function getUserList(): JsonResponse {
return response()->json(User::with('role')->get());
}
Note: You don't need to call ->toArray() when returning as a JSON response; that is done automatically
https://laravel.com/docs/9.x/eloquent-relationships#eager-loading
Then user.role.name on your front-end.
If you want user.role_name instead of user.role.name, then you'll need to define an accessor and append it:
User.php:
class User extends Model {
public $appends = ['role_name'];
public function role() {
return $this->belongsTo(Role::class);
}
public function getRoleNameAttribute() {
return $this->role->name;
}
}
Now, user.role_name will be available.

Related

Limiting retrieved columns when using withPivot on belonsToMany relationship

I have a model called Shifts with a belongsToMany relationship to a shift_employee table that acts as a pivot table to record applications for employees to shifts. I also have a scope so that I can return applications with shift objects. Here is part my Shift model:
class Shift extends Model
{
//
use SoftDeletes;
use \App\Http\Traits\UsesUuid;
protected $guarded = [];
public function applications()
{
return $this->belongsToMany(Employee::class, 'shift_employee')->as('application')->withTimestamps()->withPivot('shortlisted');
}
...
public function scopeWithApplications($query)
{
$query->with('applications');
}
...
}
My shift_employee pivot table is pretty simple and the structure is shown below. I have one extra field to determine if an application has been shortlisted:
Schema::create('shift_employee', function (Blueprint $table) {
$table->primary(['employee_id', 'shift_id']);
$table->uuid('employee_id');
$table->uuid('shift_id');
$table->boolean('shortlisted')->default(false);
$table->timestamps();
$table->foreign('employee_id')
->references('id')
->on('employees');
$table->foreign('shift_id')
->references('id')
->on('shifts')
->onDelete('cascade');
});
Below is my API show function for retrieving shift info:
public function show($id)
{
$shift = Shift::where('id', $id)
->with...()
->withApplications()
->with...()
->first();
return response([
'shift' => $shift,
]);
}
This is the response that I'm getting:
"shift": {
"id": "2b91f55b-c0ff-4bdb-abc4-02604ba6a161",
"some_field": "some_value",
...
"applications": [
{
some_field: "some_value",
...
application: {
shift_id: "2b91f55b-c0ff-4bdb-abc4-02604ba6a161",
employee_id: "some_uuid",
created_at: ...,
updated_at: ...,
shortlisted: 0
}
},
{
...
}
]
...
}
What I want to do, is to replace the whole "application" inner object with only the field "shortlisted" from the pivot table so that it looks like this:
"shift": {
"id": "2b91f55b-c0ff-4bdb-abc4-02604ba6a161",
"some_field": "some_value",
...
"applications": [
{
some_field: "some_value",
...
shortlisted: 0
}
},
{
...
}
]
...
}
How can I do that? Ideally an eloquent call to something like withPivot but that excludes other fields and does not return an object. I couldn't find it in the docs, but does something like that exist?
i think that the most straightforward way is to make independent relation based on the pivot table using pivot model:
class ShiftEmployee extends Pivot
{
protected $table='shift_employee';
}
now the new relation in Shift Model:
class Shift extends Model
{
public function shortlistedApplications()
{
return $this->hasMany(ShiftEmployee::class,'shift_id');
}
public function scopeWithShortlistedApplications($query)
{
$query->with('shortlistedApplications:shift_id,shortlisted');
}
}
now this new scope would bring the data you want
What I think you need is to only load the shortlisted attribute of your employee's application in your scopeWithApllications:
public function scopeWithApplications($query)
{
$query->with('applications.application:id,shortlisted');
}
This will still return an Application instance as a relationship, but will only load it's shortlisted attribute. Then, after retrieval, you can map your collection in order to merge the application's attribute to your employee, if that's really important. But in terms of data shortage, this will do the trick.
In your application model use withPivot method. Like this:
public function applications(){
return $this->belongsToMany('App\Application')
->withPivot('shortlisted')
->withTimestamps();}
You can use this link for more clear example
https://laraveldaily.com/pivot-tables-and-many-to-many-relationships/

Eloquent Relationship Between Tables in Laravel

I have three tables:
collections which has id, name
genre_collection which has id, genre_id, collection_id
genres which has id, name
I want to retrieve data from collections with generes.
Collections Model
class Collections extends Model{
public function genres(){
return $this->hasMany('App\Models\GenreCollectionRelationships', 'genre_id' , 'id');
}
}
generic_collection
class GenreCollectionRelationships extends Model{
public function genre(){
return $this->hasOne('App\Models\Genres', 'id', 'genre_id');
}
}
Search Controller
class SearchController extends Controller{
$collection->genres;
foreach($collection->genres as $item){
$item->genre;
}
}
This code is working fine. And the output is
Actual
"genres": [{
"id": 1,
"genre_id": 1,
"collection_id": 1,
"created_at": "2019-02-07 17:13:36",
"updated_at": "2019-02-07 17:13:36",
"genre": {
"name": "Action",
"meta": null
}
}]
Is there any way i could directly get the output as shown below
Expected
"genres": [ {
"name": "Action",
"meta": null
}]
I tried hasManyThrough, belongsToMany but nothing worked out.
Note. I am on laravel 5.7
Thanks in advance.
You could build your own query to achieve what you are looking for. Try this:
$collection = Collection
::join('genres', 'genre.id', '=', 'collections.genre_id')
->select('collections.*', 'genres.name','genre.meta')
->get();
I find your code a bit hard to follow...
Let me try and see if I understood it correctly...
You basically have two models:
Model Collection saved in table collections
Model Genre saved in table genres
Since you have a many to many relationship between them, you need a third table to link the both of them together.
By naming convention, Laravel expects you to name it based on the two models, ordered alphabetically. So to create a link between collections and genres, you would need to create a table collection_genre which has a collection_id as a reference to the collections table, and likewise a genre_id to identify the linked genre.
You can then define your relationships as follows:
class Collection extends Model {
public function genres() {
$this->belongsToMany(\App\Models\Genre::class);
}
}
and
class Genre extends Model {
public function collections() {
$this->belongsToMany(\App\Models\Collection::class);
}
}
Now, I'm not sure what your controller looks like as the question has some invalid code to it, but I suspect you want to search the genres for a given collection.
Your code could like like this:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return $collection->genres;
}
}
This would return the genres for the given collection.
If you want to format this, you could create an Eloquent Resource for this:
Class CollectionResource extends Resource {
public function toArray() {
return [
'name' => $this->name,
'meta' => $this->meta
];
}
}
In your controller you can then do:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return CollectionResource::collection($collection->genres);
}
}
in your collection model
class Collections extends Model
{
protected $table='collections';
public $primaryKey='id';
protected $fillable = ['name'];
public function genres()
{
return $this->belongsToMany('App\Model\Genres','genre_collection','collection_id','genre_id')->withTimestamps();
}
}
in your genres model
class Genre extends Model {
protected $table='genres';
public $primaryKey='id';
protected $fillable = ['name'];
public function collections()
{
return $this->belongsToMany('App\Model\Collections','genre_collection','genre_id','collection_id')->get();
}
}
You are creating many to many relationship between collections and genre using genre_collection pivot table. In that case, belongsToMany is appropriate. And you don't need any model for genre_collection table.
Collections model
class Collections extends Model
{
public function genres(){
return $this->belongsToMany('App\Models\Genres', 'genre_collection', 'genre_id', 'collection_id');
}
}
Genres model
class Genres extends Model
{
public function collections(){
return $this->belongsToMany('App\Models\Collections', 'genre_collection', 'collection_id', 'genre_id');
}
}
SearchController
class SearchController extends Controller
{
foreach($collection->genres as $item){
$item->genre; // get genre info
}
}
I'm assuming that you want to access Generic directly from collection . If this is the case you can define a many-to-many relationship in collection model directly to generic model to access it . Please refer this : https://laravel.com/docs/5.7/eloquent-relationships#many-to-many . Sorry if I'm wrong

Return if the related model has a value or not as a new field

I have Post model and related Likes item. I am trying to return the if the related model has a row for authenticated user or not. So the tables look like:
Posts
- id
- body
- etc
Likes
- id
- user_id
- likable_type
- likeable_id
Now I am return Posts eloquent with Posts::get(), however I want to return every model with a parameter inside is_liked which shows if the authenticated user has liked that post or not.
If I use ->with('likes')->where(function($q)) {} approach, it will only return me the Post that user has liked but this is not what I want.
I want every Post object to show if the authenticated user has liked it or not. Such as: Post { id, body, is_liked }
Is there any way of achieving this beside running a for loop? What is the best way to handle such scenario?
Update
class Post extends Model {
protected $appends = ['is_liked'];
public function getIsLikedAttribute() {
return $this->has('likes.user_id', '=', Auth::id())->exists();
}
public function likes() {
return $this->morphOne('App\Like', 'likeable');
}
}
class Like extends Model {
protected $fillable = [
'user_id', 'likeable_id', 'likeable_type'
];
public function likeable() {
return $this->morphTo();
}
}
Writing through this morph works, however, $this->has('likes.user_id' part returns error
Method Illuminate\Database\Query\Builder::user_id does not exist.
You can use am accessor and add that property to the append protected variable:
protected $appends = ['is_liked'];
public function getIsLikedAttribute() {
return $this->likes()->where('user_id', '=', Auth::id())->exists();
}
Now you can also check the accessor like
$post->is_liked // bool

Distant HasManyThrough

I have four Models:
User
Client
Store
Opportunity
The relationships are defined as such:
User hasMany Client
Client hasMany Store
Store hasMany Opportunity
User hasManyThrough Store, Client (this works)
The problem is that I'm attempting to access the User->Opportunity relationship via built-in Laravel relationships, but it doesn't seem as if I can do it without a custom Query or an additional user_id column on the opportunities table to allow direct access (even though one can be inferred from the Store->Client relationship). I'm also not a fan of nested foreach loops if they can be avoided.
My question:
Is there a way to go one level deeper and directly access a User's Opportunities in this scenario? The actual Model code and all relevant relationships are as follows:
User
class User extends Eloquent{
public function clients(){
return $this->hasMany('Client');
}
public function stores(){
return $this->hasManyThrough('Store', 'Client');
}
public function proposals(){
return $this->hasMany('Proposal');
}
public function opportunities(){ //This does the job, but I feel like it could be better
return Opportunity::join('stores', 'stores.id', '=', 'opportunities.store_id')->
join('clients', 'clients.id', '=', 'stores.client_id')->
join('users', 'users.id', '=', 'clients.user_id')->
select('opportunities.*')->
where('users.id', $this->id);
}
public function getOpportunitiesAttribute(){ //This just helps mimic the hasManyThrough shorthand
return $this->opportunities()->get();
}
}
Client
class Client extends Eloquent{
public function stores(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunities(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Store
class Store extends Eloquent {
public function client(){
return $this->belongsTo('Client');
}
public function opportunities(){
return $this->hasMany('Opportunity');
}
}
Opportunity
class Opportunity extends Eloquent {
public function store(){
return $this->belongsTo('Store');
}
}
I don't think there is such method in Laravel. You have to create your custom query. This custom query can be very expensive since multiple queries will be performed. Thus, the optimum solution for this, according to me, is to relate User and Opportunity with a foreign key.
However, if you don't desire to link User and Opportunity with a foreign key, then you can create a custom query to handle this. Simply add a "hasManyThrough" relation between Opportunity and Client model like,
<?php
class Client extends Eloquent{
public function store(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunity(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Then create a static function in User model.
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
public function client(){
return $this->hasMany('Client');
}
public function store(){
return $this->hasManyThrough('Store', 'Client');
}
public static function getOpportunityOfUser($userId)
{
$clients = User::find($userId)->client;
foreach ($clients as $client) {
$opportunities[] = Client::find($client->id)->opportunity;
}
return $opportunities;
}
}
Now you can access Opportunity realted to a User in one go like,
Route::get('/', function()
{
return $usersOpportunities = User::getOpportunityOfUser(1);
});
This will return all opportunity of all clients related to User with id '1'.
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function opportunities() {
return $this->hasManyDeep(Opportunity::class, [Client::class, Store::class]);
}
}

Laravel 4:: Returning models and its relationship

I would like to return the model and part of its relationship
EX::
User model
public function comments()
{
return $this->hasMany('comments');
}
Comments model
public function user()
{
return $this->belongsTo('user');
}
Can I return all comments and the user's name associated with the comment?
The desired effect is
$comment = Comments::find($id);
$comment->user;
return $comment;
This will return the one comment and the associated user full model. I just need the name of the user. And this does not works if I call Comments::all()
Thank you in advance.
You're looking for Eloquent's Eager Loading
Assuming your Comments model has a method user():
public function user()
{
return $this->belongsTo('User');
}
You should be able to do this in your controller:
$comments = Comments::with('user')->where('post_id', $post_id);
// Return JSON, as is Laravel's convention when returning
// Eloquent model directly
return $comments;
You can do the opposite as well:
Assuming your User model has a method 'comments()', like so:
public function comments()
{
return $this->hasMany('Comment');
}
Inside of your controller, you should be able to do the following, assuming your have the $id of the user available:
$user = User::with('comments')->find($id);
// Return JSON, as is Laravel's convention when returning
// Eloquent model directly
return $user;

Categories