New to Laravel - How to pass model data to a blade view? - laravel

Ok, I am totally re-writing this question, now that I am a bit more familiar with larval.
Here is my situation: I have a guitar lessons site based on larval 5.2.36, where each lesson belongs to a category, and within a lesson are several exercises. An exercise table does not have a category id as it is linked to a lesson which has a category.
Goal What I am trying to figure out is how to pass the category of the currently displayed lesson or exercise to a menu sidebar view that displays the categories, so that the category of the lesson or exercise is highlighted. For this, I need to understand how to do such a task in laravel.
From what I gathered, this is often done via controllers. However, there is no menu controller, but rather a menu composer. It contains a function
class MenuComposer
{
public function compose(View $view)
{
$minutes = 6 * 60;
$value = Cache::remember('menu-categories', $minutes, function() {
return \App\Category::with('parent')->with('children')->get();
});
$view->with('categories', $value);
}
}
Then in the menu blade file we have
#foreach ($categories as $category)
<?php $category = $category->present(); ?>
#if ($category->parent == null)
<li>{{ $category->title }}</li>
#foreach ($category->children as $child)
<?php $child = $child->present() ?>
<li class="level1">{{ $child->title }}</li>
<?php
/*
#foreach ($child->children as $grandChild)
<?php $grandChild = $grandChild->present() ?>
<li class="level2">{{ $grandChild->title }}</li>
#endforeach
*/
?>
#endforeach
#endif
#endforeach
So this is clear. I see that I can use the menu composer to pass additional data with a $view->with() call.
The question is how do I get the current category? For exercises and lessons, the routes don't have category data. They are of form
lesson/lessonid/lessontitle
or
exercise/exid/extitle
So I know I could do some sort of query of the model. But seems that wouldn't make sense, since I know there are other places in the process flow where the current cat is being passed. For instance, on an exercise page, the view is retrieving category as
$exercise->lesson->category->title
It is being passed this in exercise controller as
public function index($id, $name = null)
{
//$this->hit($id);
$exercise = $this->apiController->get($id);
$authorized = $this->isUserAuthorized();
return view('exercise/index', [
'exercise' => $exercise->present(),
'authorized' => $authorized,
]);
}
Similarly, a lesson controller passes $lesson object to lesson view as
public function index($id, $name = null)
{
//$this->hit($id);
$lesson = $this->apiController->get($id);
$subscribed = $this->request->user() && $this->request->user()->subscribed('premium');
return view('lesson/index', [
'lesson' => $lesson->present(),
'subscribed' => $subscribed,
]);
}
Based on above, seems I could modify the return statements in the lesson and exercise controller to pass the category to the menu view, but I don't see in the documentation how to do that, and I suspect the menu view is rendered before the lesson and exercise controller are called...
Also read about using service providers. middleware, etc, here: How to pass data to all views in Laravel 5?
But all these approaches seem overkill. I don't need every view to have the data. Seems to me, I need to do this somehow in the menu composer. But I don't know what method to use from the menu composer to retrieve the current lesson or exercise category. In the menu composer after debugging in phpstorm I see that the $view object for a lesson has $view->$data->$lesson->$entity.
So what I did was edited the menu composer to pass category to view:
$d=$view->getdata();
$s=array_key_exists ('lesson' , $d );
if ($s ==1) $attr = collect($d)->get('lesson');
$cat=$attr->cat();
This works since in the LessonPresenter I added function
public function cat()
{
$cat = $this->entity->category['attributes']['title'];
return $cat;
}
This works, but I feel like it is a hack. And I will have to do this for the Exercise Presenter as well. Being new to larval I suspect there has to be a more elegant way to do this. So can someone please explain how this should be done?
thanks,
Brian

You can use Facades of Laravel directly in blade templates.
Just use {! !} syntax to try and echo it. e.g: {!! Route::current() !!}
There are also similar functions of Route facade you can use.
Then, you can check your category with #if() ... #endif blocks and add something like class name within it.
Note: Don't put lots of logic in your blade files. Do it in your controller file (even in your other service classes) and pass simplest variables (e.g $isCurrentCategory) as an array to your template files using View::make() function's 2nd parameter.

Maybe this can help you
<a href="#" class="{{ (\Request::route()->getName() == 'routename') ? 'active' : '' }}">
You can also get the route prefix for example, you can check this out here:
Laravel API Docs Routing

Related

Laravel Livewire Pagination Result Disappear After Clicking Next Page/Page Number

i've been searching for a question like the one above but i can't find one, so i'm gonna make one
My problem is, when i make a very simple pagination with Laravel Livewire, the first page appear just fine, but when i click "Next Page/Page Number", the result just disappear, even though there are still more results to show, i'm absolutely got no idea, no mattter how i try.
As you can see, the simple pagination work just fine, there is next and previous button and a very big next and previous picture below
But the moment i click next page or page number, the result just disappear, the total number of result even go to 0
Here is my Component code:
namespace App\Http\Livewire;
use Livewire\Component;
use Illuminate\Http\Request;
use DB;
use Livewire\WithPagination;
class Category extends Component
{
use WithPagination;
public function render(Request $request)
{
$id = $request->id;
$product_count = DB::table('product')->where('category_id', $id)->count();
$page = $request->page ?? 1;
$product_all = DB::table('product')->where('category_id', $id)->paginate(3);
return view('livewire.category',[
'product_all' => $product_all,
'product_count' => $product_count,
'page' => $page,
]);
}
}
Here is my view code:
<div class="row">
#foreach ($product_all as $product)
<div class="col-lg-4 col-md-6 col-12 mt-4 pt-2">
#php
product($product);
#endphp
</div>
#endforeach
<!-- PAGINATION START -->
#if ($product_all instanceof \Illuminate\Pagination\LengthAwarePaginator)
{{ $product_all->links() }}
#endif
<!-- PAGINATION END -->
</div>
And a small notice, i try pagination(just normal laravel pagination) without livewire and it work perfectly fine, that's why i am really clueless of what is happening
I have already include the livewire #livewireStyles and #livewireScripts, and i can't find an answer anywhere else cause i don't see any question that match my problem, and i'm kinda new to livewire
I think your problems is related with the request parameter on the render method of the livewire component, once livewire on rerender can't access the request value, until next browser refresh.
public function render(Request $request)
instead, use livewire properties to bind the id and page with values livewire can read on rerenders
#livewire('some-component', ['category_id' => $parent_category_id, 'page' => $parent_page_value])
Like above, livewire bind this values to self properties and read from them on rerender
public $category_id, $page;
public function render()
{
$product_count = DB::table('product')->where('category_id', $this->category_id)->count();
$product_all = DB::table('product')->where('category_id', $this->category_id)->paginate(3);
return view('livewire.category',[
'product_all' => $product_all,
'product_count' => $product_count,
'page' => $this->page,
]);
}

Laravel missing parameters to update form in controller

So I have a little project, in it, there's the possibility to upload banners images to be shown on the main page. All the stuff related to the DB are already created and the option to create a banner is working, it creates the banner and then stores the image on the DB for use later. Now I'm trying to work on an edit function so I can change the description under the bannners. I have an Edit route in my controller which returns a view where I edit said banner then it calls the update function on the controller. But no matter what I put here, I'm always getting the Missing Required Parameters error once I try to Save the edit and open my controller through the Update function. Here's the code as it is now:
The route definition:
Route::resource('banner', 'BannerController');
The edit function on my controller:
public function edit($id)
{
return view('admin/edit-banners',['id'=>$id]);
}
The update function has not been implemented because I always start with a dd() function to check if everything is working fine:
public function update(Request $request, $id)
{
dd($request);
}
And here's the form line in my edit view that is trying to call the update route:
<form class="card-box" action="{{ route('banner.update',[$banner]) }}">
I also added this at the beginning of the view to store the data from the DB into a variable:
#php
use\App\Banner;
$banner = Banner::where('id','=',$id)->get();
#endphp
The $banner variable contains all the information on the banner being edited, and I can get the new description at the controller with the $request variable, so I honestly don't know what should I put here as parameters, any ideas?
The $banner variable is not a Model instance, it is a Collection.
Adjust your controller to pass this to the view instead of dong the query in the view:
public function edit($id)
{
$banner = Banner::findOrFail($id);
return view('admin.edit-banners', ['banner' => $banner]);
}
You could also use Route Model Binding here instead of doing the query yourself.
Remove that #php block from your view.
The form should be adjusted to use method POST and spoof the method PUT or PATCH (as the update route is a PUT or PATCH route) and you should adjust the call to route:
<form class="card-box" action="{{ route('banner.update', ['banner' => $banner]) }}" method="POST">
#method('PUT')
If you include $id in your function declaration then when you call the route helper it expects you to give it an id parameter. Try with
<form class="card-box" action="{{ route('banner.update',['id' => $id]) }}">
You should be able to retrieve the form data just fine form the $request variable. More info here.
The code below should be the error source. $banner variable then is an array but the update function accept object or id.
#php
use\App\Banner;
$banner = Banner::where('id','=',$id)->get();
#endphp
You should try to replay this code by this one...
#php
use\App\Banner;
$banner = Banner::find($id);
//you should put a dd here to view the contain of $banner if you like
#endphp
Hop it help...

Laravel #include controller data

I'm trying to receive data on a sidebar that is included in the blade template but i'm not getting any data delivered. I've tried adding #include('admin.sidebar',['message_counter' => $message_counter]) and in the sidebar view show as {{$message_counter}}. I'm getting a Undefined variable: message_counter.
My router:
Route::get('/admin/sidebar', [
'uses' => 'MessagesController#counter',
'as' => 'admin.sidebar'
]);
My controller
use App\Message;
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar')->with('message_counter', $message_counter);
}
My View
<span class="menu-collapsed">Messages <span class="badge badge-pill badge-primary ml-2"> {{$message_counter}} </span></span>
What i ultimately intend to do is to show the amount of unread messages in the sidebar of the administrator backend, which is #includein every page.
It may be because i'm accessing two different controllers everytime I enter any page on the admin backend.
I've looked into Including Sub-Views but i'm probably missing something silly or not understanding some key concept, help is appreciated!
Thank you!
Note: I think this is inconvenient and unrecommendable. This is just to answer the question, you can scroll down to see other answers or approach.
Controller
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar');
}
View
#php
$message_counter = App\Message::where('status', 0)->get();
#endphp
Messages <span class="badge badge-pill badge-primary ml-2"> {{$message_counter}} </span></span>
You can try like this way
In Route:
Route::get('/admin/sidebar', 'MessagesController#counter');
In Controller
use App\Message;
public function counter()
{
$message_counter = Message::where('status', 0)->get();
return view('admin.sidebar', compact('message_counter));
}
And your view is ok.. Try this and if it is not working please let me know....
With a View Composer: add this to App\Providers\AppServiceProvider#boot()
View::composer('admin.sidebar', function ($view) {
$message_counter = Message::where('status', 0)->get();
$view->with([''message_counter' => $message_counter]);
});

What is the best way to delete a record and then return to same view with slug and data?

I have an app that has articles to which comments can be attached. I now want to give the user the function to delete a comment. What I am struggling with is then returning the user to the same view after the delete.
I could do this in js but am trying to see if I can avoid it.
The article link is like this myapp/articles/1. The slug (1) being the article id. I pass along with this all comments with the same article id.
In my article view I have;
#if (count($comments) > 1)
#foreach ($comments as $comment)
<p><i class="fa fa-trash" aria-hidden="true"></i> {{ $comment['details'] }}</p>
#endforeach
#endif
In my controller I have;
public function deleteComment($id)
{
$comment= Comment::where('id', $id)->first();
$comment->delete();
return view ('myapp/articles/'); //This is where I am puzzled. How do I return user to myapp/articles/1?
}
Suggestion?
Thanks!

How to implement a generic view presenter in Laravel

I am currently using laracasts/Presenter to handle logic related to my views, this implementation is connected to a model. But I also have some generic view logic that I would like to implement, what is the best practice for creating this?
I have tried two methods, but neither feels right:
Custom class ViewHelper with static functions, called with ViewHelper::Method.
Blade include files, called with #include('includes.navicon')
Is there a better way of doing this? Or is one (or both) of the above acceptable?
Edit: We're talking simple stuff here like insert page title, run text though Markdown parser. This is an example of a function that I use on all pages, it just creates and returns a page header.
public static function PageTitle($level, $title, $small = null)
{
if ($small != null) $title = $title . " <small>" . $small . "</small>";
$html = "<div class=\"page-header\" style=\"margin-top: 0px\"><h%1\$d>%2\$s</h%1\$d></div>";
return sprintf($html, $level, $title);
}
The view presenter that I have installed makes use of the model, so to get a formatted URL for example I would use the command: {{ $article->present()->url }}, while this generic view logic should be available in all views without having to add it to all the models.
I ended up creating a base class that all view presenters extend, and move everything generic there. Functions that is required on views that do not have a model I simply added as static.
Model, I added $presenterInfo to pass information to load the correct view(s) and use as a title prefix. The rest is required by the view presenter.
use Laracasts\Presenter\PresentableTrait;
...
use PresentableTrait;
protected $presenter = 'ArticlePresenter';
public $presenterInfo = ['view' =>'articles', 'category' => 'Article'];
Baseclass, everything generic goes here. So basically everything that might be useful on multiple classes and their views.
use Laracasts\Presenter\Presenter;
class BasePresenter extends Presenter {
public static function pageHeader($level, $title, $small = null)
{
if ($small != null) $title .= " <small>" . $small . "</small>";
$html = "<div class=\"page-header\" style=\"margin-top: 0px\"><h%1\$d>$this->presenterInfo['category']: %2\$s</h%1\$d></div>";
return sprintf($html, $level, $title);
}
public function url()
{
return URL::route($this->presenterInfo['view'] . '.show', array('id' => $this->id, 'slug' => Str::slug($this->title)));
}
}
Viewclass, functions that will only be available for the selected class; in this case Article.
class ArticlePresenter extends BasePresenter {
// Example function only needed by the article class.
public function stump()
{
return Str::limit($this->content, 500);
}
}
Examples, loading view presenter data:
// Show page header level 2
{{ BasePresenter::pageHeader(2, 'Articles') }}
// Enumerate the articles and show title, stump and read more link
#foreach($articles as $article)
<article>
<h3>{{ HTML::link($article->present()->url, $article->title) }}</h3>
<div class="body">
<p>{{ $article->present()->stump }}</p>
<p>Read more...
</div>
</article>
#endforeach

Resources