how to acces nested objects in blade templates - laravel

Let me show you what I have.
//Post Controller
public function showPost(Post $post)
{
$albums = $post->album()->with('photos')->get();
$comments = $post->comments()->where('approved', '=', 1)->get();
$this->layout->title = $post->title;
$this->layout->main = View::make('home')->nest('content','posts.single', compact('post', 'comments','albums'));
}
mysql sentence executed correctly
string 'select * from albums where albums.id = '{"id":19,"title":"Post no 19","read_more":"Lorem i'... (length=786)
string 'select * from albums where albums.post_id = '19'' (length=54)
string 'select * from images where images.album_id in ('4')' (length=57)
I want to access the Photo object in blade, everything parses to the template but when I try to get the Photos
#foreach($albums->Photos as $photo)
Undefined property: Illuminate\Database\Eloquent\Collection::$Photos (View:
// post model
public function album()
{
return $this->hasOne('Album');
}
//album model
public function Photos(){
return $this->hasMany('Image');
}

Try
#foreach($albums->Photos()->get() as $photo)
{{$photo->attribute}}
#endforeach
You need to call the function that holds the relationship then use
get()
method to return an Eloquent Collection and then iterate it with a foreach loop.

#foreach($albums as $album)
#foreach($album->Photos as $photo)
<p>{{ $photo->image }}</p>
#endforeach
#endforeach
THats the answer

Related

How to get Eloquent collection if collection has data or count is > 0?

Consider this example... which gets the image of product.
This function should only get the data if it exists otherwise it should return null... because it will get the images for a product otherwise the site will display a 404 not found image...
// Product.php
public function getFileImageURL()
{
$referType = 1; //Product = 1
if(File::where('file_refer_id','=', $this->id)
->where('file_refer_type','=', $referType)
->count()>0)
{
return $imageFileItems = File::where('file_refer_id','=', $this->id)
->where('file_refer_type','=', $referType)
->get();
}
}
EDIT:
Additional details
//ProductController.php
public function show($slug)
{
$product = $this->productRepository->findProductBySlug($slug);
$productImages = $product->getFileImageURL();
return view('pages.product.show', compact('product', 'productImages'));
}
// product/show.blade.php
#if($productImages)
<img src="{{ $productImages->first()->file_url }}">
#else
<img alt="Image not found" src="{{ asset('images/products/404.png') }}">
#endif
Calling get() from Eloquent will always return to you an Eloquent collection; which means you can use collection methods on it.
Considering what you are wanting to do, the following should do the trick:
public function getFileImageURL()
{
$referType = 1; //Product = 1
$imageFileItems = File::where('file_refer_id','=', $this->id)
->where('file_refer_type','=', $referType)
->get();
return $imageFileItems->isNotEmpty()
? $imageFileItems
: null;
}
This also saves you from having to perform an extra, unneccssary, query to get the count of records.

Method Illuminate\Database\Eloquent\Collection::links does not exist

I created a model relationship between User and Message. I want to implement a list of messages for the authenticated user but I get the following error.
Method Illuminate\Database\Eloquent\Collection::links does not exist
Controller
public function index()
{
$user_id = auth()->user()->id;
$user = User::find($user_id);
return view('message.index')->with('messages', $user->message);
}
Message provider
class message extends Model
{
public function user() {
return $this->belongsTo('App\User');
}
}
User provider
public function message ()
{
return $this->hasMany('App\Message');
}
index.blade.php
#extends('layouts.app')
#section('content')
<h1>messages</h1>
#if(count($messages)>0)
#foreach ($messages as $message)
<div class="well">
<h3>{{$message->user}}</h3>
<small>Written on {{$message->created_at}} </small>
</div>
#endforeach
{{$messages->links()}}
#else
<p> no post found </p>
#endif
#endsection
Error
"Method Illuminate\Database\Eloquent\Collection::links does not exist.(View: C:\xampp\htdocs\basicwebsite\resources\views\message\index.blade.php)"
Check your view blade, that method (links()) only could be used when your data model is implementing paginate() method.
If you dont use paginate(), remove this part:
{{$messages->links() }}
If you are trying to paginate your data when it gets to the view then you need to add the paginate in your controller before passing the data to the view. Example
return $users = Users::select('id','name')->paginate(10);
with that paginate method in your controller, you can call the links method to paginate your object in view as shown below
{{$users->links()}}
hope it helps you
There are 2 ways to resolve this issue:
Either use paginate function while searching data from database:
$users = DB::table('users')->where('id',$user_id)->paginate(1);
Remove links() function from index.blade.php
{{ $messages->links() }}
Remove {{ $messages->links() }} to in your index.blade.php because {{ $messages->links() }} is supported only when you use paginate
You can do something like this in your controller file.
public function index()
{
$messages = Message::all()->paginate(5);
$user_id = auth()->user()->id;
$user = User::find($user_id);
return view('message.index')->with('messages', $messages, $user->message);
}

Trying to get property 'post_titile' of non-object

This is My Controller.
public function show(Posts $posts)
{
$page = Posts::find($posts->id);
//dd($page);
return view('web_views.index',['page' => $page]);
}
This is my view page
<h4>{{$page->post_titile}}</h4>
It's better to use Route-Model Binding I think.
Your route (if you are using resource route, it's already done):
Route::get('posts/{post}', 'PostController#show); // domain.tld/posts/1
Your method should look like this:
public function show(Post $post)
{
return view('web_views.index',compact('post'));
}
In your view:
<h4>{{ $post->post_titile }}</h4>
May be $posts->id you are passing, has no result in database so you need to check it by using if-statement
public function show(Posts $posts)
{
$page = Posts::find($posts->id);
if($page == null){
$page = ['post_title' => 'Not Available'];
}
return view('web_views.index',['page' => $page]);
}
I believe this error is because of not finding data for an ID into database. So if as per above script will pass the fetched data to view otherwise it will push an item into $page array.

Loop through nested relation in view

I'm trying to loop through some data in my blade view. This is my database setup:
What I would like to have in my view is the following:
So I can loop through the days and then order the tasks by the category they are in. I'm not experienced with Laravel and I'm having difficulties on ordering the tasks by category in the best way.
In my Days Model I have:
public function tasks()
{
return $this->belongsToMany('App\Task');
}
In my Task Model I have:
public function days()
{
return $this->belongsToMany('App\Day');
}
public function category()
{
return $this->belongsTo('App\Category');
}
In my Category Model I have:
public function tasks()
{
return $this->hasMany('App\Task');
}
In my controller I now only have:
public function index()
{
$categories = Category::all();
return view('app.blade.php', compact('categories'));
}
But how can I make sure to order them by days and then by category? Help much appreciated.
You can try as following:
$days = Days::with('tasks.category')->get();
$days = $days->map(function ($day) {
$day->tasks = $day->tasks->groupBy('category_id');
return $day;
});
Check it how it is converted after groupBy() method
dd($days);
then in your view you can do as:
#foreach ($days as $day)
{{ $day->name }}
#foreach ($days->tasks as $tasks)
{{ $tasks->first()->category->name }}
#foreach ($tasks as $task)
{{ $task->name }}
#endforeach
#endforeach
#endforeach
Note - I have not tested it but you can give it a try.

get only newest object from a model - laravel eloquent

I have two models: Supplier and Quotation. I can use a foreach to list all quotations of a supplier:
// in my `Supplier` model:
public function quotations()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id');
}
And now I want to print only the most recent quotation. The best I can figure out is this function in model:
public function newestQuotation()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id')->orderBy('offered_at','desc')->limit(1);
}
and this foreach loop:
#foreach($o->newestQuotation as $q)
#include('displaymodules.module_quotations_mini', array('bg_color' => 'bg-danger'))
#endforeach
TO DO:
The above code does the job... but how it can be done properly? I am thinking of doing a helper, but doing the function in my models seems more like a good practice.
Thank you
Edited:
maybe I wasn't clear enough... I don't want to use foreach to access the object.
What I want is a snippet like this:
#include('displaymodules.module_quotations_mini', array( 'q' => $o->latestQuotations))
I get such an object when the query has the ->first(), but not when it has '->get()`.
when I do
$latestQuotation = App\Models\Quotation:where('supplier_id', 3)->orderBy('offered_at, 'desc')->first();
to get what I want without foreach I use this this temporary solution
<?php
$latestQuotation = App\Models\Quotation::where('supplier_id', $o->id)->orderBy('offered_at', 'desc')->first();
?>
#if(isset($latestQuotation))
#include('displaymodules.module_quotations_mini', array('bg_color' => 'csch_subtle_3', 'q' => $latestQuotation))
#endif
Attempts to implement Krishan Koenig's answer
attempt #1
<?php $latestQuotations = $o->latestQuotations(); ?>
{{print_r($latestQuotations)}}
Result: domain.dev is currently unable to handle this request.
HTTP ERROR 500
attempt #2
{{$o->latestQuotations()->offered_at}}
or
{{$o->latestQuotations->offered_at}}
Result:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$offered_at (View:\resources\views\leads\_show_bestoffers.blade.php) (View: \resources\views\leads\_show_bestoffers.blade.php)
attempt #3 - my desired solution
#include('displaymodules.module_quotations_mini', array('bg_color' => 'csch_subtle_5', 'q' => $o->latestQuotations))
Result:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$offered_at (View:\resources\views\leads\_show_bestoffers.blade.php) (View: \resources\views\leads\_show_bestoffers.blade.php)
You could create a new scope in related model, call it like latest:
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc')->limit(1);
}
and then in your relationship:
public function newestQuotation()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id')->latest();
}
For example:
Quotation.php model
class Quotation extends Model
{
protected $primaryKey = 'id';
function withSupplier() {
return $this->hasMany('App\Models\Quotation', 'id', 'supplier_id')->orderBy('offered_at','desc');
}
public function newestQuotation($supplier_id){
News::with('withSupplier')->where('supplier_id', $supplier_id)->get();
}
}
in contriller:
public function pageQuotations(Quotation $quotation)
{
$quotations = $quotation->newestQuotation(1); //id Supplier
return view('displaymodules.quotations', $quotations);
}
in quotations.blade.php (example)
<table>
<tbody>
#foreach ($quotations as $quotation)
<tr>
<th>{{ $quotation->title }}</th>
<td>{{ $quotation->withSupplier()->title }}</td>
</tr>
#endforeach
</tbody>
</table>
</div>
You can create a new relatiotion in Supplier Model as:
public function newestQuotation()
{
return $this->hasOne('App\Models\Quotation', 'supplier_id')->orderBy('offered_at', 'desc');
}
Then you can query it as:
$supplieres = Supplier::with('newestQuotation')->take(20)->get();
#foreach ($supplieres as $supplier)
{{ $supplier->newestQuotation->title }} // just an example
#endforeach
Update
Try your query as:
$supplier = Supplier::where('id', $id)->first();
$latestQuotation = $supplier->newestQuotation;
Create a scope and use your existing relationship!
public function quotations()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id');
}
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc')->limit(1);
}
public function latestQuotations()
{
return $this->quotations()->latest();
}
And in your Controller you query them with
//...
$latestQuotations = $supplier->latestQuotations();
Edited Version:
// supplier model
public function quotations()
{
return $this->hasMany(Quotation::class);
}
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc');
}
public function latestQuotations()
{
return $this->quotations()->latest()->get();
}
// web route
Route::get('/', function () {
$latestQuotations = App\Supplier::first()->latestQuotations();
return view('welcome', compact('latestQuotations'));
});
// welcome view
{{ $latestQuotations }}
...worked for me.
The most simple solution is
$entity = $collection->first();
Then, whateverthe count of the collection, the result is a single object.
This allows to use terms such as $entity->id or $entity->name.

Resources