How to reuse a blade partial in a template - laravel

I would like to be able to repeat a partial a number of times within a view, with different content in each repetition.
The partial is a simple panel, with a heading, and some content. The content within each panel can vary in complexity, so I would like to be able to use the #section('content') method of passing data.
The set up I have is as follows:
panel.blade.php - The partial to be repeated.
<div class="panel">
<header>
#yield('heading')
</header>
<div class="inner">
#yield('inner')
</div>
</div>
view.blade.php - The view in which the partial is repeated
#extends('default')
#section('content')
{{-- First Panel --}}
#section('heading')
Welcome, {{ $user->name }}
#stop
#section('inner')
<p>Welcome to the site.</p>
#stop
#include('panel')
{{-- Second Panel --}}
#section('heading')
Your Friends
#stop
#section('inner')
<ul>
#foreach($user->friends as $friend)
<li>{{ $friend->name }}</li>
#endforeach
</ul>
#stop
#include('panel')
#stop
I am running into the same issue as this: http://forums.laravel.io/viewtopic.php?id=3497
The first panel is displayed as intended, but the second is simply a repeat of the first.
How can I correct this? If this is a poor method of getting this done, what would be a better way?

For Laravel 5.4, Components & Slots may be useful for you. The following solution is for Laravel 4.x, and likely <= 5.3 as well.
In my opinion, this is a silly use case for the #include syntax. The amount of HTML retyping you're saving is negligible, especially since the only potentially-complicated part of it is the inner content. Keep in mind, the more parsing that needs to be done, the more overhead your application has, also.
Also, I don't know the inner workings of the #yield & #section features, so I can't say how "proper" it is to work your includes this way. Includes typically utilize a key => value pair passed as a parameter in the include call:
#include('panel', ['heading' => 'Welcome', 'inner' => '<p>Some stuff here.</p>'])
Not the most ideal place to pack a bunch of HTML, but that's the "designed" way (as far as I know, at least).
That said...
Use the #section ... #overwrite syntax mentioned in the "Other Control Structures" part of the template docs page.
#extends('default')
#section('content')
{{-- First Panel --}}
#section('heading')
Welcome, {{ $user->name }}
#overwrite
#section('inner')
<p>Welcome to the site.</p>
#overwrite
#include('panel')
{{-- Second Panel --}}
#section('heading')
Your Friends
#overwrite
#section('inner')
<ul>
#foreach($user->friends as $friend)
<li>{{ $friend->name }}</li>
#endforeach
</ul>
#overwrite
#include('panel')
#stop

Related

Laravel Components: default content in {{ slots }}

In old-style Laravel blade templated we used to use #section('section-name') in the following way:
{{-- h1para.blade.php --}}
<h1>
#section('heading')
Heading from Template
#endsection
</h1>
<p>
#yield('details')
</p>
And then extend that template with:
{{-- content.blade.php --}}
#extends('h1para')
#section('details')
Content from content page
#endsection
In the above, my rendered HTML output would look like the following, because the "missing" 'heading' section in the extending file means that we default back to the content in the template:
<h1>Heading from Template</h1>
<p>Content from content page</p>
But in the new components way of doing things, I do:
{{-- .../components/h1para.blade.php --}}
<h1>{{ $heading }}</h1>
<p>{{ $slot }}</p>
In case you haven't gathered, the question is: how do I set a default value for a slot's contents, such that if it isn't supplied in the extending component/template, it falls back to that default?
(I've done my searches, but haven't been able to find the same question asked before)
EDIT:
I should add that I've seen the solution (in the Laravel documentation):
<h1>{{ $heading ?? 'Default Heading Here' }}</h1>
But this seems only to be appropriate if the default value is a short easy to manage string. If the default is a long stream of HTML, then it wouldn't work for my needs.
EDIT 2:
Just to reiterate: the whole point of the question is that the default content could be a long stream of HTML. Solving the problem by passing in a string (be that formatted as HTML or not) wouldn't work for my real-world needs.
I think the solution is this:
{{-- .../component/template.blade.php --}}
<div>
#if (isset($heading))
{{ $heading }}
#else
<h1>Default Heading<span class="subhead">default subheadin and lots of other html content</span></h1>
#endif
<p>{{ $slot }}</p>
</div>
It's not super elegant, but I think it's the only solution. Anyone else have a better answer, I'd love to hear it.
If you pass data like:
<x-h1para header="<span>Header content</span>">
<div>Default slot content here</div>
</x-h1para>
You can display in your component like:
<div>
<h1>{!! $heading ?? 'Default Heading Here' !!}</h1>
{{ $slot }}
</div>

Show recent blog activity on main page

Using Lektor, trying to determine how to list the most recent three blogs by published date on the main landing (root) page. I'm feeling like I should be using a macro, but I don't understand how to pass the Blogs to the page template, or if this is an example of a flowblock? I've added a section like the following to the page.ini:
[children]
model = blog-post
order_by = -pub_date, title
but can't seem to loop through them in the template (no error is thrown but doesn't iterate). Quite lost, but still consuming the documentation.
I ended up using the site.query class functionality directly in the layout template (based on the Blog quickstart).
{% for blogpost in site.query('/blog').order_by('pub_date').limit(3) %}
<div class="post col-md-4">
<div class="post-details">
<div class="post-meta d-flex justify-content-between">
<div class="date">{{ blogpost.pub_date }}</div>
</div><a href="post.html"> <!-- fix this one shortly -->
<h3 class="h4">{{ blogpost.title }}</h3></a>
<p class="text-muted">{{ blogpost.teaser }}</p>
</div>
</div>
{% endfor %}

Laravel 5.6 how to show few pictures in one article

Good afternoon,
I am working on the blog where I have detail of the article and want to show few pictures.
What I am getting at the moment:
As you can see I am getting the content and after the content pictures. Here is also the code of the blade.
<div class="container">
#foreach($articles as $article)
<article>
<h1 class="title is-1">{{$article->title}}</h1>
#foreach($article->images as $image)
<figure class="image is-128x128">
<img src="{{$image->path}}" alt="{{$image->title}}">
</figure>
#endforeach
<p>{{$article->content}}</p>
</article>
#endforeach
</div>
Because I am getting a object I cannot split the pictures.
.
It was rather hard to figure out what you want, but I'm quite sure I finally got the question.
What you wanna do is something like this:
<div class="container">
#foreach($articles as $article)
<article>
<h1 class="title is-1">{{$article->title}}</h1>
#if(count($article->images) > 0)
<figure class="image is-128x128">
<img src="{{$article->images->first()->path}}" alt="{{$article->images->first()->title}}">
</figure>
#endif
<p>{{$article->content}}</p>
#if(count($article->images) > 1)
#foreach($article->images->slice(1) as $image)
<figure class="image is-128x128">
<img src="{{$image->path}}" alt="{{$image->title}}">
</figure>
#endforeach
#endif
</article>
#endforeach
</div>
This will not only give you headline > image 1 > content > remaining images, it will also make sure you only print images when there are actually some available. The $article->images->slice(1) within the second #if() will ensure we are not using the first image a second time.
Make sure $image->path is the correct to the image file (if it's an url, skip this point).
You can check by echoing it or just dd($image->path).
For the image to be accessed, it needs to be on the public folder of laravel
If you wish to have the files in the storage folder and have it accessed publicly, you'll need to create a symlink
https://laravel.com/docs/5.6/filesystem

Pagination is not showing

I'm trying to get pagination into my view. I don't know where is the problem, the code doesn't show any errors.
Here is my controller function
public function apakskategorijas($id)
{
$apakskat = apakskategorijas::with('prece')->where('id',$id)->paginate(2);
return view ('kategorijas.apakskategorijas',compact('apakskat'));
}
View
#section('content')
#foreach($apakskat as $apk)
#foreach($apk->prece as $prec)
<div class="col-md-4 kat">
<a href="{{ url('kategorija/apakskategorija/preces/'.$prec->id) }}">
<div>
<img src="{{ URL::to($prec->path) }}">
</div>
<div class="nos">
<p>{{$prec->nosaukums}}</p>
</div></a>
<div class="price-box-new discount">
<div class="label">Cena</div>
<div class="price">{{ $prec->cena }} €</div>
</div>
<div><span>Ielikt grozā</span></div>
</div>
#endforeach
#endforeach
<center>{{$apakskat->links()}}</center> <--pagination
#endsection
dd($apakskat)
UPDATE
when i changed code in my controller to $apakskat = apakskategorijas::paginate(1); then it showed me pagination, but this doesn't work for me since i need to display items in each subcategory, with this code it just displays every item i have,it doesn't filter which subcategory is selected.
This is why i need this $apakskat = apakskategorijas::with('prece')->where('id',$id)->paginate(1); with is a function that i call which creates a relation between tables, so that it would display every item with its related subcategory.
That's the behavior of paginator in current Laravel version. When you have just one page, pagination links are not displayed.
To test this, just change the code to something like this to get more pages:
$apakskat = apakskategorijas::paginate(1);
If you want to show the first page if there is only one page, you need to customize pagination views. Just publish pagination views and remove this #if/#endif pair from the default.blade.php but keep the rest of the code as is:
#if ($paginator->hasPages())
....
#endif

retrieving rows based on another rows laravel

I am working over a basic system where blogs retrieve the image from other database table but it shows the posts not the photos
here is my controller:
$bloginfo=bloginfo::where('blog_name','=',$name)->first();
$blogposts=pageposts::where('p_id','=',$bloginfo->id)->orderBy('id','desc')->get();
foreach($blogposts as $posts){
$photoscount=pagephotos::where('b_id','=',$posts->id)->count();
$blogphotos=pagephotos::where('b_id','=',$posts->id)->get();
}
return View::make('myblog')
->with('bloginfo',$bloginfo)
->with('blogposts',$blogposts)
->with('blogphotos',$blogphotos)
->with('photoscount',$photoscount);
and here is my view:
#foreach($blogposts as $posts)
<div class="panel panel-default">
<div class="panel-body">
#if($photoscount==0)
{{ null }}
#elseif($photoscount==1)
//a bunch of code here
#else
//a bunch of code here
#endif
<h4>{{ $posts->title }}</h4>
<p>{{ $posts->description }}</p>
</div>
</div>
#endforeach
Might i suggest you looking into to relationships, this could also be your answer.
You can eager load all the data within one query.
bloginfo::with('posts', 'posts.images')->where('blog_name','=',$name)->first();
This will load the blog posts with all the information that relates to it, i.e. the posts, and their images. Look here for setting them up. HERE
As your current problem is within your for loop you overwrite the variables you are setting n times, i.e. by the amount of posts there are. So potentially you will always have different results.

Resources