Eloquent Relationship using DB Laravel - php

I've got 4 tables.
Clients
Jobs
Rounds
Job_Rounds
I've created the following relationships:
Clients > Jobs
Jobs > Rounds
Here are my models
Client
class Client extends Model
{
protected $dates = [
'created_at',
'updated_at',
'last_done'
];
public function Jobs()
{
return $this->hasMany('App\Job','client_id');
}
}
Job
class Job extends Model
{
protected $dates = [
'created_at',
'updated_at',
'last_done',
'due_date'
];
public function Round()
{
return $this->belongsTo('App\Round','round_id');
}
public function Client()
{
return $this->belongsTo('App\Client','client_id');
}
}
Round
class Round extends Model
{
protected $dates = [
'created_at',
'updated_at',
'date_scheduled',
'date_finished'
];
public function Clients()
{
return $this->hasMany('App\RoundClients', 'round_id');
}
public function Jobs()
{
return $this->belongsToMany('App\Job', 'job_rounds', 'round_id', 'job_id');
}
}
JobRound
class JobRound extends Model
{
public $timestamps = false;
}
On my round view I'm outputting all the jobs not in the round and all the jobs in the round.
I'm doing this by creating a DB::table to perform the check - see my code:
public function show($id)
{
$round = Auth::user()->rounds()->FindOrFail($id);
$not = DB::table('jobs')->whereNotIn('id', function($query) use($round) {
$query->select('job_id')
->from('job_rounds')
->where('round_id', $round->id);
})->get();
$are = DB::table('job_rounds')
->join('jobs', 'job_rounds.job_id', '=', 'jobs.id')
->select('job_rounds.*', 'jobs.*' ,'job_rounds.id as DAS')
->where('job_rounds.round_id',$round->id)
->get();
return view('app.round',compact('round','not','are'));
}
The problem I have is in my view, the Client to Job relationship for outputting the client name won't work as I have created my own DB query, I'm outputting the client information on my view like
Jobs on the round
#foreach($are as $job)
{{ $job->client->first_name }}
#endforeach
Jobs not on the round
#foreach($not as $job)
{{ $job->client->first_name }}
#endforeach
I get the error:
Variable $client not found etc

Your relationships methods starts with uppercase (which btw, you should change to start with lowercase for standard).
The problem is that your methods and the ones you call are not the same, you need to use the same case in both, like this...
#foreach($are as $job)
{{ $job->Client->first_name }}
#endforeach
Update
Besides the fact of the case, you are not using Eloquent.
You are just using the database query builder directly.
In order to use Eloquent you must use the Model class static methods, like this:
Job::whereNoIn('id', .....
Instead of this:
DB::table('jobs')->whereNotIn('id', ....

DB::table does not return an App\Job class. For this reason you cannot use relationship. ( $job->Client... )
Try make the queries using Eloquent instead.For more details, follow this offical Laravel documantation.

Related

withSum in deep nested relationship in laravel eloquent

I could not find this in the laravel docs on aggregate relationships
I was able to do something like this
private function refreshUsers()
{
$this->users = User::withSum(['taskTimeSessions'=> function ($query) {
$query->whereMonth('created_at',$this->month)
->where('is_reconciled',1);
}],'session_duration_in_seconds')
->get();
}
But now I am trying to query what is the total time a Sprint has or at the very least what the individual tasks inside a sprint have so that I can just sum the total of those somehow.
Sprint has many SprintTasks (pivot table)
SprintTask belongs to one Task
Task has many TaskTimeSessions
So I am trying to go find the total time of the TaskTimeSessions
Sprint::with([
'sprintTasks.task'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
I am not getting any errors, but not finding the result anywhere when dd
I thought i would get lucky and have something like this work
->withSum('sprintTasks.task.taskTimeSessions', 'session_duration_in_seconds')
But I am getting this error
Call to undefined method App\Models\Sprint::sprintTasks.task()
If anyone can help me out with some guidance on how to go about this, even if it doesn't include withSum it would be much appreciated.
As requested, these are the models.
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
// SprintTask
protected $fillable = [
'sprint_id',
'task_id',
'is_completed'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
public function sprint()
{
return $this->belongsTo(Task::class,'sprint_id');
}
// Task
public function taskTimeSessions()
{
return $this->hasMany(TaskTimeSession::class, 'task_id');
}
// TaskTimeSessions
protected $fillable = [
'task_id',
'session_duration_in_seconds'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
Is it possible to abstract this into the model as like
public function totalTaskTime() {
// using the relationship stuff to figure out the math and return it?
}
Looking for any advice on what the best approach is to do this.
Right now I am literally doing this in the blade and seems very bad
#php
$timeTracked = 0;
foreach ($sprint->sprintTasks as $sprintTask) {
$timeTracked += $sprintTask->task->time_tracked_in_seconds;
}
#endphp
You have a many to many relation between sprint and task
For that you can setup a direct relation belongsToMany with sprint_tasks as the pivot table
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
public function tasks()
{
return $this->belongsToMany(Task::class, 'sprint_tasks', 'sprint_id', 'task_id')->withPivot('is_completed');
}
Now you can use that relation to query your needs
Sprint::with(['tasks'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
There is a good package for Laravel for complex relationships - eloquent-has-many-deep. You can use it to build relationships through an unlimited number of tables.
composer require staudenmeir/eloquent-has-many-deep:"^1.7"
Sprint.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class Sprint extends Model
{
use HasRelationships;
public function tasks(): BelongsToMany
{
return $this->belongsToMany(Task::class, 'sprint_tasks');
}
public function taskTimeSessions(): HasManyDeep
{
return $this->hasManyDeepFromRelations($this->tasks(), (new Task())->taskTimeSessions());
}
}
Task.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Task extends Model
{
use HasFactory;
public function taskTimeSessions(): HasMany
{
return $this->hasMany(TaskTimeSession::class);
}
}
Result:
$sprints = Sprint::withSum('taskTimeSessions', 'session_duration_in_seconds')->get();

Pagination for nested items in the Laravel relationship

I am very beginner in Laravel. I use in my project Laravel 5.8.
I have this code:
class ForumCategory extends Model
{
use scopeActiveTrait;
protected $quarded = ['id'];
protected $fillable = ['company_id', 'enable', 'name', 'url_address', 'enable'];
public $timestamps = false;
public function themes()
{
return $this->hasMany('App\ForumPost', 'id_category', 'id')->where('parent_id', '=', null);
}
public function lastPost()
{
return $this->themes()->orderBy('id', 'desc')->first();
}
}
class ForumPost extends Model
{
use scopeActiveTrait;
protected $quarded = ['id'];
protected $fillable = ['company_id', 'id_category', 'parent_id', 'user_id', 'date', 'title', 'content', 'url_address', 'enable'];
public $timestamps = true;
protected $table = 'forum';
public function category()
{
return $this->belongsTo('App\ForumCategory', 'id_category');
}
public function author()
{
return $this->belongsTo('App\User', 'user_id');
}
public function postCount()
{
return $this->hasMany('App\ForumPost', 'parent_id', 'id')->where('parent_id', '!=', null)->count();
}
}
When I run this function:
ForumCategory::with('themes')->orderBy('name', 'asc')->paginate(35);
I have pagination only for pagination.
When I display the results of this function:
#foreach ($forums as $forum)
Welcome in {{ $forum->name }}<br/>
#foreach ($forum->themes as $post)
Post: {{ $post->title }}
#endforeach
<div class="paginationFormat"></div>
<div class="text-right">{{ $forums->links() }}</div>
#endforeach
I can display pagination for $ forums.
I would also like to display the pagination for $ post (data from relationships). How can this make it?
Maybe you could take a look at the Pagination documentation and examples from the Laravel docs: https://laravel.com/docs/5.8/pagination
The Laravel docs are a great place to start since everything is documented very well and there are a lot of examples.
If you want to add a page that paginates all posts, just return a view with Post::sortBy('name')->paginate() and use $posts->links().
If you have to do pagination an Eager loading constraint - it won't work, else you would have to use the constraint to execute the query as the parent Model/Object.
Refer to this link, go through it. https://laracasts.com/discuss/channels/laravel/paginate-constrained-eager-load
Use Themes as the Parent Object and paginate accordingly depending on the relationship defined in the models.
Themes::with(['forumCategory'=>function(query){
//condition here....
}])-paginate(10);
Or alternatively you have multiple paginations in one view but passed separately:
$themes = Themes::with('forumCategory')-paginate(10);
$forums = ForumCategory::with('themes')->orderBy('name', 'asc')->paginate(35);
Same issue in this link: Laravel Multiple Pagination in one page
And you might have to write an anonymous helper function to handle relative actions that involve both Models somewhere in your application.
Alternatively you could use lazyEager loading on the post model. So while you have this in your code, you could do lazy eager loading on the themes.
$forums = ForumCategory::with('themes')->orderBy('name', 'asc')->paginate(35);
$forums->load(['themes' => function($query) {
$query->take(5);
}]);
you could also utilize the loadCount() function.

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

Display table data without user auth

I'm studying event message board. I can display table data by every Users own post. however I would like to display All post too. I wrote this as $tasksall but it didn't work. Could someone teach me what is wrong?
AController.php
public function index()
{
$tasks = Auth::user()
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
$tasksall =
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
return view('tasks', [
'tasks' => $tasks, 'tasksall' => $tasksall
]);
}
Task.php (model)
class Task extends Model
{
protected $casts = [
'is_complete' => 'boolean',
];
protected $fillable = [
'title',
'is_complete',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
AController.php I add this code
public function person()
{
return $this->belongsTo('App\Models\Task');
}
public function getData()
{
return $this->id . ':'/ $this->person->name.')';
}
index.blade.php I add this code
{{ $task2->getData() }}
You can just write a query to get all the task using eloquent to get all the tasks.
$tasksall = Task::all();
Have a look at this link.
Also for you case I think the problem is you are getting task from the User model so you $task will contain only task related to that particular user as you have a belongsTo relation of task with user.
For Your case to get name of User from task.
//Task model
class Task {
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
Then you can query like this in your controller.
$task = Task::find($id);
$name = $task->user->name;

Laravel: returning results from multiple related tables using eloquent

I'm using Laravel 4 and in particular I'm looking for an answer that uses eloquent ORM.
I have a table "tasks" which containers a client_id and a user_id assigned to each row.
client_id refers to a client on a "clients" table and user_id refers to a user on a "users" table.
What I want to do: show all tasks and display the "clients" name and "users" first_name
So the result would look like this in my (blade) view:
#foreach($tasks as $task)
<tr>
<td>{{ $task->user->first_name }}</td>
<td>{{ $task->client->name }}</td>
<td>{{ $task->description }}</td>
</tr>
#endforeach
The above view spits out the $task->client->name perfectly fine but unfortunately shows a "Trying to get property of non-object" when I add the line $task->user->first_name
My controller looks like this:
$tasks = Task::with(array('user', 'client'))->get();
return View::make('index', compact('tasks'));
As I understand it my models make a difference too, so my models look like this:
class Task extends Eloquent {
protected $guarded = array();
public static $rules = array();
public function client() {
return $this->belongsTo('Client');
}
public function user() {
return $this->belongsTo('User');
}
}
And:
class User extends Eloquent implements UserInterface, RemindableInterface {
public function task()
{
return $this->hasMany('Task');
}
}
And:
class Client extends Eloquent {
public function projects(){
return $this->hasMany('Project', 'client_id');
}
}
Any ideas on how to make this work? I've been scratching my head for a while - also note I'm not a database relationship pro so the simpler the explanation the better :)
I just worked through this and learned quite a few things myself. What I did was setup a many to many relationship between users and clients and created a pivot table for handling the relationship called tasks which also stores the description for each task.
It was too much to type here, but you can check out my code at http://paste.laravel.com/Fpv
Many-to-many relationships can be done like this with Eloquent:
class User extends Eloquent implements UserInterface, RemindableInterface {
public function client()
{
return $this->belongsToMany('Client', 'tasks', 'client_id')->withPivot('description');
}
}
and the inverse relationship...
class Client extends Eloquent {
public function users()
{
return $this->belongsToMany('User', 'tasks', 'user_id');
}
}
Haven't tested this, but it should be correct.

Categories