how can we append two attributes by one function? - php

I want to add two appends variables in laravel model. by using one getVariableNameAttribute() function.
Is this possible?
MY motto is, I want to reduce SQL query load 2 times to into 1 time
class Like extends Model{
use HasFactory;
protected $fillable = ['like_id', 'like_type', 'liked', 'user_id'];
protected $appends = ['likes','dislikes'];
protected function getLikesAttribute($type)
{
return \DB::table("likes")
->select(\DB::raw("SUM(liked) as likes"))
->where('like_type',self::class)->where('like_id',$this->id)->first()->likes;
}
public function getDislikesAttribute()
{
return \DB::table("likes")
->select(\DB::raw("SUM(!liked) as dislikes"))
->where('like_type',self::class)->where('like_id',$this->id)->first()->dislikes;
}}

Combining both methods to return an object is possible.
protected function getVariableNameAttribute()
{
return \DB::table("likes")
->selectRaw("SUM(liked) as likes")
->selectRaw("SUM(!liked) as dislikes")
->where('like_type', self::class)
->where('like_id', $this->id)
->first();
}
$...->variable_name->likes
$...->variable_name->dislikes

Related

Laravel 5.8: Display eloquent items sorted based on timestamp

I'm not sure, how this is called, so I'll explain it as good as possible.
I've a ticket system, where I display all comments in one section. In a different section, I display related information like "Supporter changed", "Ticket title changed", "Status of ticket changed" and so on.
Current rendered (unstyled) HTML: https://jsfiddle.net/2afzxhd8/
I would like to merge these two sections into one, that those related information are displayed between the comments of the ticket. Everything (comments + related information) should be displayed sorted based on the created_at timestamp.
New target rendered (unstyled) HTML: https://jsfiddle.net/4osL9k0n/
The ticket system has in my case these relevant eloquent models (and tables):
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Tickets extends Model
{
use SoftDeletes;
protected $fillable = [
'tracking_number', 'customer_id', 'category_id',
'priority_id', 'subject', 'status_id', 'is_done',
'supporter_id'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function status() {
return $this->belongsTo(TicketStatuses::class, 'status_id');
}
public function priority() {
return $this->belongsTo(TicketPriorities::class, 'priority_id');
}
public function category() {
return $this->belongsTo(TicketCategories::class, 'category_id');
}
public function supporter() {
return $this->belongsTo(User::class, 'supporter_id');
}
public function operations() {
return $this->hasMany(TicketOperations::class, 'ticket_id');
}
public function comments() {
return $this->hasMany(TicketComments::class, 'ticket_id');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class TicketComments extends Model
{
use SoftDeletes;
protected $fillable = [
'ticket_id', 'text', 'user_id', 'is_html',
'email_reply', 'internal_only'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function ticket() {
return $this->belongsTo(Tickets::class, 'id', 'ticket_id');
}
public function user() {
return $this->belongsTo(User::class, 'user_id');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class TicketOperations extends Model
{
use SoftDeletes;
protected $fillable = [
'ticket_id', 'user_id', 'ticket_activity_id',
'old_value', 'new_value'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function ticket() {
return $this->belongsTo(Tickets::class, 'ticket_id');
}
public function activity() {
return $this->belongsTo(TicketActivities::class, 'ticket_activity_id');
}
public function user() {
return $this->belongsTo(User::class, 'user_id');
}
}
Please don't care about the CSS - it is styled in my case. It's just not relevant here.
Any idea, how I need to update my view to be able to build my target HTML?
As per my understanding, you have data that retrieved from multiple models.
So what you can do is to, merge the informations into a new array:
For example, consider the data regarding the ticket history is being stored in an array named:
$arrTicketHistory;
And consider, that the information regarding the ticket updates is being stored in an array named:
$arrTicketUpdates;
Merge these two arrays and assign the result in another array, say:
$arrDatesAndIDs;
Now try sorting the array $arrDatesAndIDs on the basis of timestamp i.e. created_at. Then display the result with a simple for loop.
You can add a custom parameter in the arrays $arrTicketUpdates and $arrDatesAndIDs, just for the sake of uniqueness. It might help you to identify which type of information it is, regarding the ticket.
You can use the array function array_msort(), a php function, to sort a multidimensional array.
I just found this answer, but this one has one big issue: It overwrites in worst-case some objects with different objects and this results in possible missing objects in the collection.
From the Laravel documentation: Collections:
The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection.
Due to this, I had to update the logic to this:
$ticket = Tickets::where('tracking_number', '=', $request->tracking_number)->first();
$comments = $ticket->comments;
$operations = $ticket->operations;
$history_unsorted = new Collection();
$history_unsorted = $history_unsorted->merge($comments);
$history_unsorted = $history_unsorted->merge($operations);
$history = $history_unsorted->sortBy('created_at');
This avoids, that the original collection gets overwritten.
With this, I can simply loop over $history:
#foreach($history as $history_item)
#if ($history_item instanceof App\TicketOperations)
<!-- Ticket Operation -->
#else
<!-- Ticket Comment (Text) -->
#endif
#endforeach

Laravel - using inRandomOrder() on belongsToMany RelationShips

in this code as:
$latestHerbsInformation = \App\ContentCategories::find('14')->contents->take(5);
which that work fine i want to use inRandomOrder() to get random rows from contents, Contents and ContentCategories are belongsToMany RelationShips and this code:
$latestHerbsInformation = \App\ContentCategories::find('14')->contents->inRandomOrder()->take(5);
don't work for me.
Contents model:
class Contents extends Model
{
use Sluggable;
protected $table = 'contents';
protected $guarded = ['id'];
public function categories()
{
return $this->belongsToMany(ContentCategories::class);
}
}
ContentCategories model:
class ContentCategories extends Model
{
protected $table = 'contents_categories';
protected $guarded = ['id'];
public function contents()
{
return $this->belongsToMany(Contents::class);
}
}
i get this error:
Method inRandomOrder does not exist.
how can i solve this problem? Thanks in advance
The correct query is:
\App\ContentCategories::find('14')->contents()->inRandomOrder()->take(5)->get();
Because this will execute the query and will return a collection:
\App\ContentCategories::find('14')->contents

Obtaining a list of non-linked models, in many_to_many relations, using Laravel Eloquent

I have a User-Roles model, using Laravel 4 where a user can have many roles, using Eloquent. I can access all roles linked to a user easily using this code :
class User extends Model {
protected $table = 'user';
protected $fillable = array('name');
public function rolesLinked() {
return $this->hasMany('App\UserRoleLink', 'user_id');
}
}
I've been trying to obtain the roles that are not linked to a user, to display on the specific user's page in a select box. Using this function, included in the User class.
public function rolesNotLinked() {
$user = this
$roles = Roles::whereDoesntHave('App\UserRoleLink',function($query) use ($user){
$query->where('user_id',$user->id);
});
}
The problem is, calling this function gives me the following error.
Call to undefined method Illuminate\Database\Query\Builder::App\UserRoleLink()
I've tried using has with < 1 to see if the function was problematic, but after reading this and the online source code, the function call pretty much does what I've tried.
Is something wrong in my function call, or have I messed up configurations somewhere?
For reference, here are my other Model classes:
class UserRoleLink extends Model{
protected $table = 'user_role_link';
protected $fillable = array('role_id','user_id);
public function role() {
return $this->hasOne('App\Role', 'role_id');
}
}
class Role extends Model{
protected $table = 'role';
protected $fillable = array('name');
}
EDIT: I've found out that I messed up by fillables when I copy-pasted. It didn't fix the issue, but I guess that's one step closer.
To use whereDoesntHave method, you must add the relation in your Role Model.
class Role extends Model{
protected $table = 'role';
protected $fillable = array('name');
public function UserRoles() {
return $this->hasMany('App\UserRoleLink', 'id');
}
}
Also, the whereDoesntHave method first parameter is not thte model but the function of the relation:
public function rolesNotLinked() {
$user = this
$roles = Roles::whereDoesntHave('UserRoles',function($query) use ($user){
$query->where('user_id',$user->id);
});
}

Laravel Eloquent nesting

I'm trying to return nested JSON response, need to connect multiple tables, but for now, I'm just trying with 3, the levels can go deeper. My models are following:
Update #1:
class Sport extends Eloquent {
protected $table = 'sportovi';
protected $fillable = ['id', 'sport_eng'];
public $timestamps = false;
public function liga(){
return $this->hasMany('League', 'sport_id');
}
}
class League extends Eloquent {
protected $table = 'lige';
protected $fillable = ['league_id', 'liga', 'sport_id'];
public $timestamps = true;
public function mec(){
return $this->hasMany('Match', 'match_id');
}
}
class Match extends Eloquent {
protected $table = 'mecevi';
protected $fillable = ['match_id', 'home', 'away', 'kotime', 'day', 'kolo', 'sport_id', 'league_id', 'date', 'long_id'];
public $timestamps = false;
public function liga(){
return $this->belongsTo('Match', 'league_id');
}
}
If I do:
$sportovi = Sport::with('liga')->get();
return $sportovi;
everything is normal, children of "lige" are nested where they should be, as shown on the link here, but, if I try to add the Match, like this:
$mecevi = Sport::with('liga.mec')->get();
I get a "mec" node, which is an empty array, as shown here, instead going one level deeper, like in the previous example.
I've also tried making multiple with() conditions which throws an error
Call to undefined method Illuminate\Database\Query\Builder::mec()
Update: Still the same, mec:[], empty array.
I'm using Laravel 4.2.
From my understanding, you want to get all the matches of leagues
The problem might be in the second parameter in hasMany function
public function mec(){
return $this->hasMany('Match', 'league_id');
}
The second parameter need to be foreign_key
public function mec(){
return $this->hasMany('Match', 'match_id', 'league_id');
}
source: http://laravel.com/docs/4.2/eloquent#one-to-many
Figured it out, my table relations were off, after giving all the conditions, like
return $this->hasMany('Comment', 'foreign_key', 'local_key'); everything started working as it should. Thanks everybody.

Ordering of nested relation by attribute with laravel

Hi there i'm trying to sort a collection by attribute of the relation.
This is my model
class Song extends \Eloquent {
protected $fillable = ['title', 'year'];
public function artist(){
return $this->hasOne('Artist','id', 'artist_id');
}
}
class SongDance extends \Eloquent {
protected $table = 'song_dances';
protected $fillable = ['rating'];
public function dance(){
return $this->belongsTo('Dance', 'dance_id');
}
public function song(){
return $this->belongsTo('Song', 'song_id');
}
}
class Dance extends \Eloquent {
protected $fillable = ['name'];
public function song_dances(){
return $this->hasMany('SongDance','dance_id','id');
}
public function songs(){
return $this->belongsToMany('Song', 'song_dances', 'dance_id', 'song_id');
}
}
this is how far i'm by now:
$dance = Dance::find(1);
$songs = $dance->songs()
->with('artist')->whereHas('artist', function ($query) {
$query->where('urlName','LIKE','%robbie%');})
->where('song_dances.rating', '=', $rating)
->orderBy('songs.title','asc')
->where('songs.year', '=', 2012)
->get();
Yeah i just could add a ->sortBy('artist.name'); to the query, but he result-collection can be quite big (about 6000 items) therefore i would prefer a databased sorting.
is there a possibility to do this?
Since Eloquent's relations are all queried separately (not with JOINs), there's no way to achieve what you want in the database layer. The only thing you can do is sort the collection in your app code, which you've already dismissed.
If you feel you must sort it in the database then you should write your own join queries instead of relying on Eloquent's relations.

Categories