Laravel get whole database table with its associates - php

Is there the possibility to get whole database's table with it's associates?
My example
class Gambler extends Model
{
use HasFactory;
public function horse()
{
return $this->belongsTo(Horse::class, 'horse_id', 'id');
}
}
I am fetching data of Gamblers with axios, which is being returned with this code in controller
public function getGamblers () {
echo Gambler::all();
}
However I would like to get all associated Horses as well. I can get one pretty easy just like this
public function getGamblers () {
echo Gambler::find(1)->horse;
}
but maybe there is possibility to do something like this, which in my case does not work
public function getGamblers () {
echo Gambler::all()->horse;
}

You should use return in Controllers, not echo.
Because you are using axios, a JsonResponse will be more appropriate:
public function getGamblers () {
return response()->json(Gambler::all(), 200);
}
Gambler::all() will return a Collection of Gambler.
On frontend, loop over the data you get from Laravel:
let gamblers = [];
axios.get('/your-get-gamblers-url')
.then(function (response) {
gamblers = response.data;
})
gamblers.forEach(gambler => console.log(gambler))
If for some reson you need to do something with each Gambler in php, you can loop over the collection like this:
$gamblers = Gambler::all()
foreach($gamblers as $gambler) {
//$gambler->horse
}

Related

Adding a condition to a long string of object relations in Laravel / Eloquent

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);

Laravel - relationship

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 [];
}

Laravel - "Method Illuminate\\Database\\Query\\Builder::recipes does not exist

I've looked at a few similar issues on SO, but I can't work out why recipes is being called?
I'm building a recipes website, and the user can create tags, create a recipe, create ingredients which are assigned to the recipe, and will be able to create steps which use the ingredients.
The following are my models:
Ingredient.php
class Ingredient extends Model
{
protected $touches = ['recipes'];
public function recipes(){
return $this->belongsToMany('App\Recipe', 'recipe_ingredients', 'ingredient_id', 'recipe_id');
}
public function step(){
return $this->belongsToMany('App\Step', 'step_ingredients');
}
}
Recipe.php
class Recipe extends Model
{
public function ingredients(){
return $this->belongsToMany ('App\Ingredient', 'recipe_ingredients', 'recipe_id', 'ingredient_id');
}
public function tags(){
return $this->belongsToMany('App\Tag', 'recipe_tag');
}
public function user(){
return $this->belongsTo('App\User');
}
public function steps(){
return $this->hasMany('App\Step');
}
}
Step.php
class Step extends Model
{
protected $touches = ['recipes'];
public function recipe(){
return $this->belongsTo('App\Recipe');
}
public function ingredient(){
return $this->belongsToMany('App\Ingredient', 'step_ingredient');
}
}
The following is my StepsController which I'm saving to from my steps/create.blade.php file via an Ajax submit with jQuery.
use Illuminate\Http\Request;
//use Session;
use App\Recipe;
use App\Tag;
use App\Step;
use App\Ingredient;
class StepsController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function create($recipe_id)
{
$recipe = Recipe::find($recipe_id);
return view('steps.create')->withRecipe($recipe);
}
public function store(Request $request)
{
$step = new Step;
$step->recipe_id = 2;
$step->step_no = 2;
$step->is_prep = 1;
$step->duration = '00:14:01';
$step->method = 'test';
$step->save();
$data = [
'success' => true,
'message'=> 'Your AJAX processed correctly',
'ing' => json_decode($request->ingredients),
'desc' => $request->description
] ;
return response()->json($data);
}
}
I'm using static values for the new Step to make sure that it writes to the db correctly, and it is.
If I comment out writing the step to the db, and just leave the $data array and return response... then it works fine, and I get the success response returned to the view.
When I include it, I get the error in the console:
[Error] Failed to load resource: the server responded with a status of 500 (Internal Server Error) (steps, line 0)
"message": "Method Illuminate\\Database\\Query\\Builder::recipes does not exist.",
But I'm not sure where the recipes method is being called?! I think it has something to do with my relationships in the model?
Any insight would be extremely appreciated.
Not sure if it's required, but the following is the part of my script that I"m using so submit data with Ajax.
$("#addStepNew").click(function() {
var step_ingredients = JSON.stringify(stepIngredients)
var step_description = $('#stepDescription').val();
// var prep_step = $('input[name=prepStep]:checked').val();
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
type: "post",
data: {ingredients: step_ingredients, description: step_description},
dataType:'json',
url: "{{ route('steps.store', ['id' => $recipe->id]) }}",
success: function (data) {
$("#ajaxOutput").html('<code>Description output: '+data.desc+'</code>');
$.each(data.ing, function (key, value) {
$("#ajaxOutput").append('<br><code>'+value.ingredient_name+'</code>');
});
},
error: function (xhr, ajaxOptions, thrownError) {
console.warn(xhr.responseText);
alert(xhr.status);
alert(thrownError);
}
});
});
It looks like it's the protected $touches = ['recipes']; from the Step model.
I guess it's trying to update a recipe that it's associated to, but that isn't being defined with a recipe_id.

Laravel Eloquent Relationship hasMany hasOne

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']);

Cache Eloquent Relationship query

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.

Categories