How to assert that Laravel Controller returns view with proper data? - laravel

I need to know how to assert that Laravel Controller returns view with proper data.
My simple controller function:
public function index() {
$users = User::all();
return view('user.index', ['users' => $users]);
}
I am using functions such as assertViewIs to get know if proper view file is loaded:
$response->assertViewIs('user.index');
Also using asserViewHas to know that "users" variable is taken:
$response->assertViewHas('users');
But I do not know how to assert if retrieve collection of users contain given users or not.
Thanks in advance.

In tests I would use the RefreshDatabase trait to get a clean database on each test. This allows you to create the data you need for that test and make assumptions on this data.
The test could then look something like this:
// Do not forget to use the RefreshDatabase trait in your test class.
use RefreshDatabase;
// ...
/** #test */
public function index_view_displays_users()
{
// Given: a list of users
factory(User::class, 5)->create();
// When: I visit the index page
$response = $this->get(route('index'));
// Then: I expect the view to have the correct users variable
$response->assertViewHas('users', User::all());
}
The key is to use the trait. When you now create the 5 dummy users with the factory, these will be the only ones in your database for that test, therefore the Users::all() call in your controller will return only those users.

Related

How do I pass a value in my Route to the Controller to be used in the View in Laravel?

I have 2 entities called Match and Roster.
My Match routes are like this
http://localhost:8888/app/public/matches (index)
http://localhost:8888/app/public/matches/14 (show)
In order to view/create the teams for each specific match I added the routes for the match roster like this:
Route::get('/matches/'.'{id}'.'/roster/', [App\Http\Controllers\RosterController::class, 'index']);
Now I need that {id} i have in my URL to pass it to the Controller here:
public function index()
{
return view('roster.index');
}
I need that for a couple of things. First I need to do a search on the Roster table filtering by a column with that value, so I can display only the players that belong to that match.
Second, I need to pass it on to the view so I can use it on my store and update forms. I want to add or remove players from the roster from that same index view.
How can I do that?
#1 You can get the route parameter defined on ur routes via request()->route('parameter_name').
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
}
#2 You can pass the data object via using return view(file_name, object)
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
// query what u want to show
// dunno ur models specific things, so just simple example.
$rosters = Roster::where('match_id', '=', $id);
// return view & data
return view('roster.index', $rosters);
}
#3 It can be done not only index but also others (create, store, edit, update)
In addition, STRONGLY RECOMMEND learn Official Tutorial with simple example first.
Like a Blog, Board, etc..
You need to know essentials to build Laravel App.
Most of the time, I prefer named routes.
Route::get('{bundle}/edit', [BundleController::class, 'edit'])->name('bundle.edit');
In controller
public function edit(Bundle $bundle): Response
{
// do your magic here
}
You can call the route by,
route('bundle.edit', $bundle);

Returning same variable to every controller in laravel

I need to send the same result to almost every view page, so I need to bind the variables and return with every controller.
My sample code
public function index()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.index', compact('drcategory','locations'));
}
public function contact()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.contact', compact('drcategory','locations'));
}
But as you see, I need to write same code over and over again. How can I write it once and include it any function whenever I need?
I thought about using a constructor, but I cannot figure out how I can implement this.
You are able to achieve this by using the View::share() function within the AppServicerProvider:
App\Providers\AppServiceProvider.php:
public function __construct()
{
use View::Share('variableName', $variableValue );
}
Then, within your controller, you call your view as normal:
public function myTestAction()
{
return view('view.name.here');
}
Now you can call your variable within the view:
<p>{{ variableName }}</p>
You can read more in the docs.
There are a few ways to implement this.
You can go with a service, a provider or, like you said, within the constructor.
I am guessing you will share this between more parts of your code, not just this controller and for such, I would do a service with static calls if the code is that short and focused.
If you are absolutely sure it is only a special case for this controller then you can do:
class YourController
{
protected $drcategory;
public function __construct()
{
$this->drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
}
// Your other functions here
}
In the end, I would still put your query under a Service or Provider and pass that to the controller instead of having it directly there. Maybe something extra to explore? :)
For this, you can use View Composer Binding feature of laravel
add this is in boot function of AppServiceProvider
View::composer('*', function ($view) {
$view->with('drcategory', DoctorCategory::orderBy('speciality', 'asc')->get());
$view->with('locations', Location::get());
}); //please import class...
when you visit on every page you can access drcategory and location object every time
and no need to send drcategory and location form every controller to view.
Edit your controller method
public function index()
{
return view('visitor.index');
}
#Sunil mentioned way View Composer Binding is the best way to achieve this.

Laravel Backpack - getting current record from crud controller

In my crud controller I am trying to get the name of the person who is currently being edited.
so
http://192.168.10.10/admin/people/93/edit
In the people crud controller
public function setup() {
dd(\App\Models\People::get()->first()->name)
}
This returns the first person not the person currently being edited.
How do I return the current person (with an id of 93 in this example)
Ok, So since you use backpack look into CrudController to see how the method looks:
public function edit($id)
{
$this->crud->hasAccessOrFail('update');
$this->data['entry'] = $this->crud->getEntry($id);
$this->data['crud'] = $this->crud;
$this->data['fields'] = $this->crud->getUpdateFields($id);
$this->data['id'] = $id;
return view('crud::edit', $this->data);
}
So now you can overwrite the edit function and change whatever you want. You can even create a custom edit page if you so wish.
Setup on the other hand is usually used to add things like
$this->crud->addClause(...);
Or you can even get the entire constructor and put it in the setup method because setup call looks like this:
public function __construct()
{
// call the setup function inside this closure to also have the request there
// this way, developers can use things stored in session (auth variables, etc)
$this->middleware(function ($request, $next) {
$this->setup();
return $next($request);
});
}
So you could do something like \Auth::user()->id;
Also it's normal to work like this. If you only use pure laravel you will only have access to the current id in the routes that you set accordingly.
Rahman said about find($id) method. If you want to abort 404 exception just use method findOrFail($id). In my opinion it's better way, because find($id)->name can throw
"Trying to get property of non-object error ..."
findOrFail($id) first fetch user with specified ID. If doesn't exists just throw 404, not 500.
The best answer is:
public function edit($id)
{
return \App\Models\People::findOrFail($id);
}
Good luck.
you need person against id, try below
public function setup($id) {
dd(\App\Models\People::find($id)->name);
}

Laravel Controller member variable

Is there any possibility in laravel to keep the state of the controller object?
In every example I found, the controller looks the following way:
class MyController extends Controller {
public function getView(){ //return the view }
public function postData() { //save the data }
}
What I would do is to call a service which loads specific data from my data base and return it to the view. In the example above this should be done within the getView() function. What if I need the same data in my postData() function.. Then I have to make another database call in the postData function. It is not possible to have a member variable in 'MyController' and to load the data only once for the class because of routing in laravel. When I call via routing the getView function I get another instance of MyController than I get if I call postData. Is there a possibility to call a specific function only once for the whole controller and to get access to this values from all the functions within the controller?
Is there a possibility to call a specific function only once for the
whole controller and to get access to this values from all the
functions within the controller?
As per my understanding it it not possible. Actually any function of controller is being called via routes. When your any route has been called every time the new object of controller is being created. But it has other way of round. You can use Cache. You can implement it as below:
Call to your specific function of controller.
Get the data from the database.
Store it in Cache for other functions.
In other functions check is data available in Cache? then get from Cache else call your database function to get the data.
Simply in coding as below:
Use Cache;
class MyController extends Controller {
public function getView(){
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
return the view
}
public function postData() {
$data = null;
if(Cache::has('data')) {
$data = Cache::get('data');
} else {
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
}
}
}
Syntax Description:
Cache::put('name_to_save_data',$your_variable_to_save,for_number_minutes);

Models accessible only for authenticated user THAT CREATED THEM (Laravel)

I'm writing a software application to let the people have their own private archive of cooking recipes.
The RecipeController constructor contains:
$this->middleware('auth')
because only registered users can use recipes, but I need to protect also the access to the models.
The point is that users can view and modify only their own recipes.
Example: The user TortelliEngineer can create a recipe "Tortelli Secret Recipe" using the model Recipe; he can view, update and delete his recipe(s), but nobody else can see his precious "Tortelli Secret Recipe".
So, which is the cleanest way?
I added a user_id attribute to the model Recipe.
I must use this parameter every single time that I ask to the database for a Recipe (goodbye "findOrFail" by ID)
That means that every time I make a request I must access the Request object that contains User that contains User_id
using Auth::id() EVERY SINGLE TIME that I need one (or n) recipe
Like this:
class RecipeRepository{
public function all(){
return Recipe::where('user_id', Auth::id())
->orderBy('created_at', 'asc')
->get();
}
public function find($recipe_id){
return Recipe::where('user_id', Auth::id())
->where('id', $recipe_id)
->firstOrFail();
}
Is that correct? Do you hate me for this? Do you know better or more correct ways to do it?
Most of the time I make a method inside the model to check if someone is authorised, owner etc.. of something.
An example would be:
// User model
public function owns_recipe($recipe)
{
return ($recipe->user_id == $this->id);
}
You can call this at the very beginning in of the methods of your controller:
// Controller
public function index (Request $request)
{
$recipe = Recipe::find($request->id); // Get recipe
$user = ... // Get user somehow
if (!$recipe) App::abort(404); // Show 404 not found, or something
if (!$user->owns_recipe($recipe)) App::abort(403); // Show 403 permission denied, or something
... // Do whatever you want :)
}
While there are many ways of approaching this, Laravel does provide some built-in methods for handling general authentication of actions. In the first place I'd do something along the lines of what you intended (have a getRecipesByOwner method in RecipeRepository) and you can pass the user to it from the injected Request object:
// RecipeController
public function index(Request $request)
{
$recipes = $this->recipeRepo->findRecipesByOwner($request->user());
}
In addition though, I'd recommend creating policies to manage whether or not a user is capable of updating/deleting/viewing individual recipes. You can then authorize their actions in the controllers/blade templates/etc. via built-in methods like:
// Controller
public function update(Request $request, Recipe $recipe)
{
$this->authorize('update', $recipe);
}
// Blade template
#can('update', $recipe)
#endcan
The documentation is available at: https://laravel.com/docs/5.3/authorization#creating-policies

Resources