Foreach on value blade Laravel [duplicate] - php

This question already has answers here:
How can i iterate over attributes in laravel models?
(2 answers)
Closed last year.
I have a problem with Laravel when I try to display my data via my view...
Here is my code to browse everything with my view :
#foreach($recipes as $t=>$d)
{{$t}} // doesn't display all the key
#endforeach
{{$recipes->LABEL}} // work great
Their is the result : incrementing preventsLazyLoading exists wasRecentlyCreated timestamps
Their is my controller and the model :
Model :
/**
* Get a recipe from its primary id : recipe_id
* If fail it return 404 exception
* Else return the collection with all the data of a recipe
*
* #param integer $id id of the recipe in the db
* #return Collection the collection that have been finded by his id
*/
public static function getRecipe($id)
{
return self::findOrFail($id);
}
Controller :
function getRecipe($id)
{
return view('recipe_detail')->with('recipes', Recipe::getRecipe($id));
}
Is their any solution to display with foreach and with call the key ?
Thanks

You have a single object $recipe that you are trying to loop on in your blade file.
This method:
public static function getRecipe($id)
{
return self::findOrFail($id);
}
returns the single object based on the $id. If you want a collection, skip the $id and use get() or all(), something like:
public function getRecipes(){
return self::all()
}
Or, just call it right in the controller without need for the method in the Model: $recipes = Recipe::all();
You can then loop on $recipes in the blade file because it is now a collection.

If you want to get all the "attributes" of the Model then you have to ask the Model for them; you can use the getAttributes method:
#foreach ($model->getAttributes() as $key => $value)
...
#endforeach

Related

Return relationships in Laravel show route

When I create a CRUD controller, this is the show route created by default:
/**
* Display the specified resource.
*
* #param \App\Team $team
* #return \Illuminate\Http\Response
*/
public function show(Team $team)
{
//
}
$team is an object here, an instance of Team. If I do this I have the correct object passed to blade:
public function show(Team $team)
{
return view('admin.teams.show', ['team' => $team]);
}
But, Team has a many-to-many relationship with another model called Player, and this relationship is defined as such from the Team side:
public function players() {
return $this->belongsToMany(Player::class);
}
In my show method, I'd like to return the $team with its related players. But since $team is already an object and not a query builder, it's too late to do something like
$team->with('players')
So how do I get the related players here? I know I can do something like:
public function show(Team $team)
{
$team_extended = Team::where('id', $team['id'])->with('players')->first();
return view('admin.teams.show', ['team' => $team_extended]);
}
But it feels like hacking a functionality that should be there by default. Is there a built-in Laravel way to do this or am I just inventing hot water and should take the approach I used in my solution above?
If you've already got your Team model loaded, you can load a relationship without having to completely re-create it using the ->load() method:
public function show(Team $team){
$team->load("players");
return view("admin.teams.show", ["team" => $team]);
}
Note however, this isn't required unless you need to modify the default content of $team->players. When you trying to access $team->players say in your admin.teams.show view, if that property doesn't already exist (as it would using ->with(["players"]) or ->load("players"), Laravel will load it automatically.

how to get the current id of show function in laravel

in my controller in my show function in laravel i want the get the id that shows in browser show when i browse it it shows like this
http://localhost:8000/admin/invoices/1
i want to get that "1" and use it in show controller like below
public function show(Invoice $invoice)
{
$clients = Invoice::with('user','products')->get();
$invoice_id = 1;
$invoices = Invoice::with('products')->where('id', '=', $invoice_id)->firstOrFail();
return view('admin.invoices.show', compact('invoice','invoices'),compact('clients'));
}
and put it instead of $invoice_id so when every my client visit this page only sees the related invoice products . thanks you for help
If you're actually getting an instance of Invoice passed to your show method then it likely means you have Route-Model Binding set up for your project. Laravel is looking at the defined route and working out that the ID part (1) should map to an instance of Invoice and is doing the work to grab the record from the database for you.
The Invoice object passed through should refer to an item in your database with the ID of 1, so to get the ID that was mapped in the route you can simply just do:
public function show(Invoice $invoice)
{
echo $invoice->id; // This should be 1
Laravel supports route model binding out of the box these days, but in earlier versions you had to set it up in app/Providers/RouteServiceProvider.php. If you don't want it, try replacing your show method signature with this:
public function show($id)
{
echo $id; // Should be 1
By removing the type-hint you're simply expecting the value that was given in the route parameter and Laravel won't try to resolve it out of the database for you.
Simple way you may try this.
//Define query string in route
Route::get('admin/invoice/{id}','ControllerName#show')
//Get `id` in show function
public function show(Invoice $invoice,$id)
{
$invoice_id = $id;
}
Try using $invoiceId
public function show(Invoice $invoice, $invoiceId)
{
$clients = Invoice::with('user','products')->get();
$invoices = Invoice::with('products')->findOrFail($invoiceId);
return view('admin.invoices.show', compact('invoice','invoices'),compact('clients'));
}
do this if you want to get the url segment in controller.
$invoice_id = request()->segment(3);
if you want this in view
{{ Request::segment(3) }}
Goodluck!
Usually happens when giving a route name different from the controller name
Example:
Route::resource('xyzs', 'AbcController');
Expected:
Route::resource('abcs', 'AbcController');

Am I doing eager loading correctly? (Eloquent)

I have a method that needs to pull in information from three related models. I have a solution that works but I'm afraid that I'm still running into the N+1 query problem (also looking for solutions on how I can check if I'm eager loading correctly).
The three models are Challenge, Entrant, User.
Challenge Model contains:
/**
* Retrieves the Entrants object associated to the Challenge
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
Entrant Model contains:
/**
* Retrieves the Challenge object associated to the Entrant
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function challenge()
{
return $this->belongsTo('App\Challenge', 'challenge_id');
}
/**
* Retrieves the User object associated to the Entrant
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
and User model contains:
/**
* Retrieves the Entrants object associated to the User
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
The method I am trying to use eager loading looks like this:
/**
* Returns an array of currently running challenges
* with associated entrants and associated users
* #return array
*/
public function liveChallenges()
{
$currentDate = Carbon::now();
$challenges = Challenge::where('end_date', '>', $currentDate)
->with('entrants.user')
->where('start_date', '<', $currentDate)
->where('active', '1')
->get();
$challengesObject = [];
foreach ($challenges as $challenge) {
$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all();
$entrantsObject = [];
foreach ($entrants as $entrant) {
$user = $entrant->user;
$entrantsObject[] = [
'entrant' => $entrant,
'user' => $user
];
}
$challengesObject[] = [
'challenge' => $challenge,
'entrants' => $entrantsObject
];
}
return $challengesObject;
}
I feel like I followed what the documentation recommended: https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
but not to sure how to check to make sure I'm not making N+1 queries opposed to just 2. Any tips or suggestions to the code are welcome, along with methods to check that eager loading is working correctly.
Use Laravel Debugbar to check queries your Laravel application is creating for each request.
Your Eloquent query should generate just 3 raw SQL queries and you need to make sure this line doesn't generate N additional queries:
$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all()
when you do ->with('entrants.user') it loads both the entrants and the user once you get to ->get(). When you do ->load('user') it runs another query to get the user. but you don't need to do this since you already pulled it when you ran ->with('entrants.user').
If you use ->loadMissing('user') instead of ->load('user') it should prevent the redundant call.
But, if you leverage Collection methods you can get away with just running the 1 query at the beginning where you declared $challenges:
foreach ($challenges as $challenge) {
// at this point, $challenge->entrants is a Collection because you already eager-loaded it
$entrants = $challenge->entrants->sortByDesc('current_total_amount');
// etc...
You don't need to use ->load('user') because $challenge->entrants is already populated with entrants and the related users. so you can just leverage the Collection method ->sortByDesc() to sort the list in php.
also, You don't need to run ->all() because that would convert it into an array of models (you can keep it as a collection of models and still foreach it).

Laravel get model without appends attributes

I'm working with Laravel 5.3 and in a Model Post, I have an appends attributes :
/*
* toJson
*/
protected $appends = ['count'];
And the magic method :
public function getCountAttribute()
{
return Offer::where('id', '=', $this->id)->count();
}
So, when I get a Post model with eloquent like Post::get(), and get the return with json for example, I ALWAYS have this attribute count in my object.
How can I specify if I want or not this or another appends atribute ?
I checked how Eloquent models get serialized and unfortunately list of fields to be appended is not shared between all instances of given Model and the only way I see to achieve what you need is to iterate through the result set and explicitly enable append for selected attributes:
$posts = Post::all();
foreach ($posts as $post) {
// if you want to add new fields to the fields that are already appended
$post->append('count');
// OR if you want to overwrite the default list of appended fields
$post->setAppends(['count']);
}
You could get the model's attributes with $post->getAttributes() which returns an array of attributes before any appends

Get next / prev element in Eloquent

I have a model for Product and Catalogue. They have many to many relationship, therefore I am using a pivot table called 'items'. I have a route:
/something/something/{catalogue}/{product}
I am passing the product model and catalogue model to the view, which outputs the product information and catalogue information - pretty straight forward. Now I need to have 2 buttons - 'next' and 'previous' to navigate through products in the catalogue.
So one way to do this, I thought I would create 2 methods on the Catalogue model that would accept the current product model and return next / prev model based on ID:
public function prevProduct($product){
$prevProdId = Product::where('id', '<', $product->id)->max('id');
return Product::find($prevProdId);
}
public function nextProduct($product){
$nextProdId = Product::where('id', '>', $product->id)->min('id');
return Product::find($nextProdId);
}
Now this works fine, but as you can see, it retrieves the next product and previous product from the Product table in the database, not the catalogue.
To get all the products in the catalogue, can be done like so: $this->items (on Catalogue model) or $catalogue->items (from view).
I somehow need to get next / prev items from the Catalogue, but can't figure out how. Any suggestions would be much appreciated.
You can filter Collection, like this:
$next = $item->id + 1;
$catalogue->filter(function($item) use ($next ) {
return $item->id == $next;
})->first();
I use global method added to Collection class:
/**
* Returns first object from collection which meets attribute criteria
*
* #param string $attributeName name of attribute we are looking for
* #param string $attributeValue value of attribute that have to be met
* #return \Illuminate\Database\Eloquent\Collection
*/
public function findByAttribute($attributeName, $attributeValue)
{
return $this->filter(function($item) use ($attributeName, $attributeValue) {
return $item->{$attributeName} == $attributeValue;
})->first();
}
In this case I would use this method this way:
$catalogue->items->findByAttribute('id', $next);
In both examples I assume You have $item object to reffer to.
You can use Pagination
Catalogue::first()->items()->paginate(1);
Actually, it was much simpler. This did the trick:
$nextProdId = $this->items()->where('product_id', '>', $product->id)->min('product_id');
return Product::find($nextProdId);
I had to add () after 'items' to enable 'where' clause and make it searchable basically.

Categories