Passing data to views without "with()" in view composers - php

I love the way of passing data to views in Laravel. But I don't use the "with" methode, I prefer to pass all my data as the second argument in the view helper function:
$data = [
'name' => Auth::User() -> name
]
return view('dashboard', $data);
Now it's very easy to use my data in the view:
Hello {{ $name }}
There's no need to do
Hello {{ $data['name'] }}
But here is my problem:
I want to do the same in a view composer. But the only way I have seen to pass data to views with view composers is this:
public function compose(View $view)
{
$data = [
'name' => Auth::User() -> name
]
$view -> with('data', $data);
}
But this requires me to do
Hello {{ $data['name'] }}
in my view, which I don't want. I want to use the short syntax. So is there a way to pass it like I described above? As second argument of the view function?
Thanks

Just step through each part of your array with a foreach loop:
foreach($data as $key => $value) { $view->with($key, $value); }

In answer to your question about just calling the view function an passing values, the answer is no, you can't do it that way.
Think about what you're doing in your first example. When you call view('template', $data), you're calling a helper function defined in the Laravel core. What is that function doing? It's instantiating a view, probably by calling View::make()->with(). In other words, behind the scenes it's doing the thing you want to avoid.
Now how would that work with a view composer? I imagine it would go something like this:
In your controller, you call view('template', $somedata).
You now have a view object with $somedata included.
On the way to rendering the HTML page, Laravel calls your view composer, passing along the view object you created a moment ago.
Then you call view($newdata) (or something like that - I'm not clear on what the syntax would be) and it would attach the new data to the existing view object. But this is not something that the Laravel developers have done. That's not to say it couldn't be done, it is just not a use case that they considered.
What you can do is step through your new data in the view composer and add the individual values to the existing view, like this:
foreach ($newdata as $key => $value) {
$view->with($key, $value);
}

Related

Laravel 5 - Where should I write this function, and how?

Goodevening,
I have an overview page where I display all my project items.
I have a model (Project.php) a controller (ProjectController) where I send the variable $project (this includes all the information for each project) to the specific view.
Note: Each project has a own row in the database. (Quit obvious I guess)
Now I also have a table 'tasks' related to a specific project. In my view I wanna display how much of the total tasks are 'done'. (This is doing with the column 'done' (true/false, boolean)).
Now because I have a foreach function in my view (to display each individuele project) I can make the function in the view. But the Laravel framework is there for reasons. And writing out a lot of php in a view isn't the right way.
But, where should I make this function? And how can I use it in my foreach (in the view). Ofcourse I can make a new foreach in my model or controller and send that variable to my view. But then I can't use that one in my view-foreach as things will get mixed up.
I don't know how/where I can set up a function like this on a clean way.
Kinds regards,
Dylan
You can create a function in app directory(forexample app/Helpers/), or wherever you want and name it yourHelperFunction.php.
After creating that file, Laravel won’t recognize the file until it is registered inside composer.json file. Add files array inside autoload section.
.
.
.
"autoload": {
"files":[
"app/Helpers/yourHelperFunction.php"
]
}
.
.
.
then do composer dump autoload and you are ready to use the function inside the blade
Use Blade engine, not Core PHP loops.
It is wise to use blade engine then core php codes.
You can see the documentation
Laravel 5 Blade Template
Thanks for the help. I've figured it out (I guess?).
I have this function in my Project Model.
public function getTaskDonePercentage($id) {
$tasks = Task::where('project_id', $id)->count();
$tasks_done = Task::where([
['project_id', $id],
['status', 'done']
])->count();
if ($tasks_done > 0) {
$calc = ($tasks_done / $tasks) * 100;
}
else {
$calc = '100';
}
return round($calc) . '%';
}
Then in my view I just call the function in the foreach like:
#foreach
{{ $project_item->getTaskDonePercentage($project_item->id) }}
#endforeach
Tbh I still wanna know if this approach is a good one. (Or, if not, why it isnt?).
Thanks!

Laravel equivalent or alternative for CodeIgniter

I am trying to figure out how to do the equivalent of the following in Laravel that I would do in CodeIgniter all the time to build views:
$section = $this->load->view('pages/about', $data, TRUE);
This would allow me to echo $section in another view file and then when that view was called the normal way, it would render it. I am not sure how to do something like this in Laravel.
UPDATE
I figured it out. What I was needing was Laravel's HtmlString class to take a string and convert it to html markup to the view file.
You would need to use the View Facade, so make sure to include it with an "Use" statement in your Controller, but basically is this:
$html = View::make('pages/about', $data)->render();
The render() method will just render the view in HTML, instead of returning it as a Response object like the view() helper function does.
There are several ways to do so, try this:
return view('admin.profile', $data);
Read through this doc:
https://laravel.com/docs/5.5/views

Laravel: call static function inside blade template

I found very strange situation. I have a collection with some results and I wanna grab for each of that results the saved in cache information. For that I have a Cache class, which has one static function get(). Unfortunately I am not receiving anything when I call it in the foreach loop of my Blade template.
#foreach($prognoses as $sport_prognose)
<?php
$pr = Cache::get(Config::get('variables.cache.prediction'), ['id' => $sport_prognose['id']]);
print_r($pr);
die();
?>
#endforeach
If I call the same function inside the Controller is display me the needed information, but not as in the example above.
Why is that ?
You can use cache() and config() and other global helpers instead of facades to avoid this kind of problem.
Inside the blade template, you can write something like this:
{{ $pc::getProducts($ship->products) }}
Notice the use of variables. Obviously getProducts is a static method inside the controller, and $ship->products is a variable coming from an array. Let's make it simple: suppose $ship->products is 1, and getProducts is this:
static function getProducts($id) { echo "id is $id; }
If you ran this script, you'd get an error because the template lacks the value of $pc. How do you get around this? You need to pass the value of $this to the template:
return View::make('shipping.index')->with(['pc' => $this, 'shipping' => $shippings);
Here shipping.index is the template, and pc is getting a value of $this, which allows $pc to get accceess to getProducts inside the blade template.

(laravel) supply mocked data to view when testing?

Suppose I have a blade template:
// resources/views/fragments/foo.blade.php
<p>{{ $foo }}</p>
(This is obviously a very stripped-down version of my question. I can't see why I'd ever need to test a blade template that simple.) In my controller, I would normally populate that template like this:
// app/Http/Controllers/FooController.php
$parameters = ['foo' => ''];
return view('fragments.foo', $parameters);
The problem is I'd like to write a phpunit test to ensure that template renders successfully when I pass it a specific value for $foo, but I can't figure out how to send data to the view when running the unit test. I tried this:
// tests/TestFooController.php
$this->withSession(['foo' => 'bar'])->visit('/foo')->assertViewHas('foo', 'bar');
but that test failed. How can I pass my own data to the view from within my test?
Finally googled around enough to get a workaround:
// tests/TestFooController.php
$parameters = ['foo' => 'bar'];
view()->composer('fragments.foo', function ($view) use ($parameters) {
$view->with($parameters);
});
$this->visit('/foo')
->assertViewHasAll([
'foo' => 'bar',
]);
The cool thing (depending on how much independence you like when testing) is that if my FooController passes other variables, but I only override one of them in the test's view composer, the other non-overridden values will still be there.

Secure and clean code in laravel 5.1

In fact i have a question about the clean of code
i try to get some value in blade file , I Am confused between two approaches
i think both are right but i need to know who's clean and secure more with the reason
First approach in my blade directly using Eloquent
#foreach
(Auth::user()->company->country->cities as $city) {{$city->name}}
#endforeach
Second approach using Injecting Services by create this method in my model and use it in my blade using laravel 5.1 Injecting Services
public function getCity()
{
foreach(Auth::user()->company->country->cities as $city) {
return $city->name ;
// OR
return $city ;
// i think this is one of benefits to use this approach
// because in my view i can use getCity()->id or getCity()->name
}
}
Thanks For Your Time .
Your second approach wouldn't work, because the function will finish while returning the name of the first city (or the first city itself). To make it work you could rewrite it, so that it returns all cities and loop through them in blade.
So if you use that function your code might look like:
#foreach($serviceName->getCities() as $city)
{{ $city->name }}
#endforeach
which is a nice thing, because the view doesn't have to care about where the cities will come from. If you use such a service on different views, it will be much easier to update.
Regarding security: There is no difference between those two approaches. As long as you print your output using the '{{ }}' operator. It'll prevent possible XSS attacks.
I think The best way that achieves MVC Design pattern whatever your code
public function getCities()
{
//in your model model
return $cities = Auth::user()->company->country->cities;
}
public function index()
{
//call return getCities();
}
//finally in your view loop over $cities
#foreach ($cities as $city)
{{$city->name}}
#endforeach
The best place to get your data from the model is in the controller, and then pass the data to the view:
This is a key-point of any MVC architecture that brings separation of concerns: your controller's purpose is to get the data from the model and to pass it to the views. The purpose of the view is to get the data from the controller and to present it. So, the only thing a view needs is a variable passed from the controller
This way the app logic is kept in the controller and it will be easy for you to mantain your application. So:
In your controller:
public function index()
{
//get data from model
$cities = Auth::user()->company->country->cities;
//pass the data to the view
return View::make('your_view', ['cities' => $cities] );
}
Then, in your view:
#foreach ($cities as $city)
{{$city->name}}
#endforeach
If you have setup eloquent relationship correctly then you should get the cities by using the following code
foreach(Auth::user()->cities as $city)
{
{!! $city->whatever !!}
}

Categories