I'm trying to figure out how I can get a nested relationship but without any success.
Modal: Workout
public static function getWorkout($workout_id = null)
{
if ($workout_id) {
return Workout::whereId($workout_id)->with('exercises.sets')->first();
}
return [];
}
public function exercises()
{
return $this->belongsToMany('App\Models\Exercise');
)
Modal: Exercise
public function sets()
{
return $this->hasMany('App\Models\Set');
}
This solution gives me all sets based on "exercise_id". I need to get only the sets within the workout.
If I do this it works, the problem is now how I should get the ID of the workout to pass. I've tried to put the relation in the Workout Model as well but then the response of sets will get outside the exercise array.
public function sets()
{
return $this->hasMany('App\Models\Set')->where('workout_id', 5);
}
Try the following snippet where I specify a condition for the eager load.
public static function getWorkout($workout_id = null)
{
if ($workout_id) {
return Workout::whereId($workout_id)->with(['exercises.sets' => function($query) use($workout_id)
{
$query->where('workout_id', $workout_id);
}])->first();
}
return [];
}
Related
Essentially I am just trying to add a where condition to this request where I get a "Phase" with a bunch of its children (i sudo'd it up a bit) :
public function show($projectId, $phaseId)
{
return Phase::with('headers.subheaders.lines.values')->findOrFail($phase);
}
I want to do something like this:
public function show($projectId, $phaseId)
{
return Phase::with('headers.subheaders.lines.projectValues')
->where('headers.subheaders.lines.projectValues.project_id', '=' , $projectId)
->findOrFail($phaseId);
}
I've tried various variations of this :
return Phase::with(['headers.subheaders.lines.projectValues' => function ($query) use ($projectId) {
$query->where('project_id', $projectId);
}])->findOrFail($phaseId);
But I can't find the magical combination of syntax to get this working properly. I normally get the error that project_id is not an attribute of phase for the last example... I've tried giving it the full path twice but it doesn't seem to like it... Maybe I'm just being dumb and theres a simple solution...
Edit :
Some of the relationships:
class Line extends Model
{
// Other stuff
public function projectValues()
{
return $this->hasMany(ProjectValues::class, 'question_id');
}
}
class QuestionValue extends Model
{
// Other stuff
public function project()
{
return $this->belongsTo(Project::class);
}
public function line()
{
return $this->belongsTo(Line::class);
}
}
have you tried using a chain of whereHas:
return Phase::with('headers.subheaders.lines.projectValues')
->whereHas('headers',function ($query)use($project_id){
$query->whereHas('subheaders',function ($query2)use($project_id){
$query2->whereHas('lines',function ($query3)use($project_id){
$query3->whereHas('projectValues',function ($query4)use($project_id){
$query4->where('project_id','=',$project_id);
});
});
});
})
->findOrFail($phaseId);
I Have this 3 tables like below :
Tools
Parts
Part_details
it is my the table structure :
Tool -> has many -> parts. part -> has many->part_details.
Tool : id*, name; Parts : id*, name, tool_id; part_details: id, part_id, total;
Question :
Using laravel Model, how can I get Tool with One part that has biggest total on parts_details ??
// Tool Model
public function parts(){
return $this->hasMany(Part::class);
}
// Part Model
public function part(){
return $this->belongsTo(Tool::class);
}
public function part_details(){
return $this->hasMany(PartDetail::class);
}
// PartDetail Model
public function part(){
return $this->belongsTo(Part::class);
}
Now query the Tool model
$tools = Tool::with('parts')->withCount('parts.part_details')->get();
$toolWithMaxCount = $tools->filter(function($tool) use ($tools){
return $tool->parts->max('par_details_count') === $tools->max('parts.part_details_count');
})->first();
You can improve this with adding some raw bindings to optimise it. I think you got the idea.
Tool model
public function parts() {
return $this->hasMany('App\Part');
}
Part Model
public function details() {
return $this->hasMany('App\PartDetail');
}
public function tool() {
return $this->belongsToMany('App\Tool');
}
Detail Model
public function part() {
return $this->belongsToMany('App\Part');
}
Controller
$tools = Tool::with('parts', 'parts.details')
->find($id)
->max('parts.part_details');
Use the the hasManyThrough Relationship to get the all part details related to tool and then you can check the one by one record and get the highest total of the tool part.
// Tool Model
public function partsdetails()
{
return $this->hasManyThrough('App\PartDetail', 'App\Part','tool_id','part_id');
}
In Your controller
$data = Tool::all();
$array = [];
if(isset($data) && !empty($data)) {
foreach ($data as $key => $value) {
$array[$value->id] = Tool::find($value->id)->partsdetails()->sum('total');
}
}
if(is_array($array) && !empty($array)) {
$maxs = array_keys($array, max($array));
print_r($maxs);
}
else{
echo "No Data Available";
}
I'm trying to build an alternative relationship that returns all records instead of only related records. I have tried returning a query builder, but that doesn't work, it must be a relationship. What should I return to make this work?
public function devices()
{
if ($this->admin) {
// return all devices relationship instead
} else {
return $this->belongsToMany('Device', 'permissions');
}
}
Fiddle: https://implode.io/XXLGG8
Edit: I'd like to continue building the query in most cases, not just get the devices.
The devices() function in your model is expected to return a relation, you shouldn't add the if statement there. Make your devices() function like this:
public function devices()
{
return $this->belongsToMany('Device', 'permissions');
}
In your User model add a new function:
public function getDevices() {
if($this->admin === true) {
return Device::all();
}
return $this->devices();
}
Now you can do:
$admin->getDevices(); // will return all devices
$user->getDevices(); // will return only relations
I actually went a slightly different way and used a scope:
protected function scopeHasAccess($query, User $user)
{
if ($user->admin) {
return $query;
}
return $query->join('permissions', 'permissions.device_id', "devices.id")
->where('permissions.user_id', $user->user_id);
}
Add devices accessor method to the User model and implement your logic there.
public function getDevicesAttribute() {
if ($this->admin) {
return Device::all();
}
return $this->getRelationValue('devices');
}
See updated "fiddle".
I am trying to get my head around Laravel's relationships but having difficulty with the last part of what I'm trying to achieve.
My tables are like so:
pagetemplates
id
pages
id
pagetemplate_id
pagetemplate_blocks
id
pagetemplate_id
pagetemplateblocks_content
id
page_id
page_template_block_id
So when loading a page out of the DB I need to get it with pagetemplates, with it's blocks and with the content.
Here's my code so far:
Page.php
public function pageTemplate()
{
return $this->belongsTo('PageTemplate', 'pagetemplate_id')->with('blocks');
}
public function pagetemplateblockcontent()
{
return $this->belongsToMany('PageTemplateBlockContent', 'pagetemplateblocks_content', 'page_id', 'page_template_block_id')->withTimestamps();
}
public function pagecontent()
{
return $this->hasMany('PageTemplateBlockContent', 'page_id')->with('pagetemplateblock');
}
PageTemplate.php
public function page()
{
return $this->hasMany('Page', 'pagetemplate_id');
}
public function blocks() {
return $this->hasMany('PageTemplateBlock', 'pagetemplate_id')->orderBy('sort_order', 'asc')->with('blockcontent');
}
PageTemplateBlock.php
public function pagetemplate() {
return $this->belongsTo('PageTemplate', 'pagetemplate_id');
}
public function blockcontent() {
return return $this->hasOne('PageTemplateBlockContent');
}
PageTemplateBlockContent.php
public function pagetemplateblock()
{
return $this->belongsTo('PageTemplateBlock', 'page_template_block_id');
}
However, the issue is with the content, if I try this it returns one instance of PageTemplateBlockContent, which is the same for all of the pages. Although there should be a different PageTemplateBlockContent for each Page. I'm not sure how to get around this, so any ideas would be greatly appreciated.
UPDATED
My Controller calls
$this->pageRepo->where('id', $id)->with('pageTemplate');
"pageTemplate" returns the following:
return $this->belongsTo('PageTemplate', 'pagetemplate_id')->with('blocks');
"blocks" returns the following:
return $this->hasMany('PageTemplateBlock', 'pagetemplate_id')->orderBy('sort_order', 'asc')->with('blockcontent');
"blockcontent" returns the following:
return $this->hasOne('PageTemplateBlockContent');
So this means it's never hitting the "pagetemplateblockcontent" which Joost suggested creating.
Change
public function pagetemplateblockcontent()
{
return $this->belongsToMany('PageTemplateBlockContent', 'pagetemplateblocks_content', 'page_id', 'page_template_block_id')->withTimestamps();
}
to
public function pagetemplateblockcontent()
{
return $this->belongsToMany('PageTemplateBlockContent', 'pagetemplateblockscontent_page', 'page_id', 'page_template_block_id')->withTimestamps();
}
and create an table page_pagetemplateblockcontent with two primary keys like so.
$table->integer("page_id")->unsigned();
$table->integer("page_template_block_id")->unsigned();
$table->primary(['page_id', 'page_template_block_id']);
How can I cache this Eloquent query:
dd($user->roles);
Because above will somehow trigger the $user->roles() query I assume.
I have tried with this:
public function roles() {
return \Cache::remember('user_' . $this->id . '_roles', 10, function() {
return $this->hasMany('App\Role');
});
}
But it does not work, because it has to return a array, not eloquent query.
Any suggestions?
Here is my approach:
public function bookmarks(): HasMany
{
return $this->hasMany(Bookmark::class);
}
protected function getBookmarksCacheKey(): string
{
return sprintf('user-%d-bookmarks', $this->id);
}
public function clearBookmarksCache(): bool
{
return Cache::forget($this->getBookmarksCacheKey());
}
public function getBookmarksAttribute(): Collection
{
if ($this->relationLoaded('bookmarks')) {
return $this->getRelationValue('bookmarks');
}
$bookmarks = Cache::rememberForever($this->getBookmarksCacheKey(), function () {
return $this->getRelationValue('bookmarks');
});
$this->setRelation('bookmarks', $bookmarks);
return $bookmarks;
}
You can't store a relationship in the cache. You need to cache the actual data retrieved from the database. So you'll have something like this:
public function roles()
{
return \Cache::remember('user_' . $this->id . '_roles', 10, function()
{
return $this->hasMany('App\Role')->get()->toArray();
});
}
And now you have to access it as a method, not a property, because it's not returning a relation anymore (and Eloquent would throw an exception):
$user->roles();
Now you should get an array as you want.
If you want to cache user together with its roles you can do it this way:
$user = User::find(1);
$user->load('roles');
Cache::put('users_'.$user->id, $user, 10);
I don't know why, but you need to use load here instead of with. If you used with you would get error that you cannot cache PDO instance.