Laravel Controller Type Hinting - php

After creating a model with -mcr (php artisan make:model Institution -mrc), the show function in controller was scaffolded as:
/**
* Display the specified resource.
*
* #param \App\Organization\Institution $institution
* #return \Illuminate\Http\Response
*/
public function show(Institution $institution)
{
return view('institutions.show', ['institution' => $institution]);
}
The return view... was inserted by me. I was expecting it to have it populated with the object whose id was sent in the parameters.
/institutions/1
But, after using dd($institution), I verified that it has the ID, not the object.
Shouldn't this variable return me the object?

This is called Route Model Binding. Your route will need to look something like:
Route::get('institutions/{institution}', 'InstitutionController#show');
and then as per your controller
public function show(Institution $institution)
{
return view('institutions.show', compact($institution))
}
You can read more on this here.
I imagine your route had the parameter called {id} rather than {institution}.

Replace the parameter of show function
public function show(Institution $institution)
{
return view('institutions.show', compact($institution))
}
becomes
public function show($id)
{
$institution = App\Institution::findOrFail($id);;
return view('institutions.show', compact('institution'));
}
and in your routes
Route::get('institutions/{id}', 'InstitutionController#show');

Related

Run where()->get() on Nova Action fields()

I'm trying to provide a Select list with only records that are related to the model via a pivot table.
While building a time tracker/budgeting software for a client I have two models I'm working with called Budgets and Projects that are joined together with a pivot table. (So budgets, projects, and budget_project)
I'm trying to display all projects that are related to a selected Budget (from the Budget resource when calling an action) on a Select field. I can't figure out how to pass the model->id into the fields function. I will then be running some code to analyze the Projects associated with the given Budget and creating a bunch of records that extend across the date range and other relationships.
Please help!
I'm looking for something like this...
class CreateSchedule extends Action
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
return Action::message('all good');
}
/**
* Get the fields available on the action.
*
* #return array
*/
public function fields()
{
$budgetProject = BudgetProject::where('budget_id',$this->id)->get();
foreach($budgetProject as $bp){
$projectArray[$bp->project_id] = $bp->project->name;
}
return [
Select::make('Project','project_id')->options($projectArray),
];
}
}
For me it it works like this
pass the id in the Resource class
(new LogsDiffAction($this->id))
Create a constructor in Action class to receive this parameter
protected $model;
public function __construct($model)
{
$this->model = $model;
}
and in fields you can do
if ($this->model) {
$entity = Activity::find($this->model);
$model = json_decode($entity->properties, true);
$partial = view('admin.nova.log-diff-partial', ['model' => $model])->toHtml();
return [
Heading::make($partial)->asHtml(),
];
}
I face a similar issue when I was working in the newsletter section where I have Template and Campaign models
You can add your model by doing this if you want to get recorded data
Notice onlyOneTabeRow function is an inline action and it's Mandatory to pass model
public function actions(Request $request)
{
return [
(new CreateCampaign($this))
->onlyOnTableRow()
];
}
Now you can receive them in CreateCampaign action contractor
public function __construct($model)
{
$this->template = $model;
}
And without making any request to the database you can get current record data like this
public function fields()
{
return [
Text::make('Name', 'name')
->default(function ($request) {
return $this->template->name;
})
->rules('required'),
Text::make('Subject', 'subject')
->default(function ($request) {
return $this->template->subject;
})->rules('required'),
Textarea::make('Content', 'content')
->default(function ($request) {
return $this->template->content;
})->rules('required')->showOnIndex(),
Hidden::make('Template Id', 'template_id')
->default(function ($request) {
return $this->template->id;
})
];
}
Here is a photo of what I want once I clicked on inline action Create Campaign in the first record I get a form with the specific record I want displaying in action form
For anyone who will struggle with Nova, I had to do something very similar.
Just as other have suggested, You need to do the following steps.
Pass whatever you need in the Action class as parameter while registering action.
( new MyAction($this->myParam) )
Make a constructor in your action class and accept the parameter.
protected $receivedParam;
public function __construct($param)
{
$this->receivedParam = $param;
}
You can then use your parameter in your fields() to do whatever you need to do with it.
Note For Actions initiated From Lens
However, While initiating Action from Lens, this will not work as you won't simply get the parameter while doing ( new MyAction($this->someParam) ). In the context of Lens, You need to first beat the Std Object that pops up and then you need to dig down the resource and then its attributes from $this.
// pass entire $this here and filter down in the Action Class
return [(new GiveMediaAccountAccessViaLens($this))]; // registering action from Lens
// Action Class
protected $receivedParam;
public function __construct($receivedParam)
{
$this->receivedParam = $receivedParam; // $receivedParam is $this from MediaAccountFilterByClient Lens
}
public function fields()
{
if(!$this->receivedParam->resource instanceof Model)
{
// If you are trying to generate a dropdown/select then you should set your options to
// empty array. $options = [];
$options = [];
}
else
{
// Here, $this->receivedParam->resource contains the Model Object, by calling
// getRawOriginal() on it, You will get the Original Attributes array which contains
// key value pair, like "some Attribute => "value"
// $this->receivedParam->resource->getRawOriginal()['someAttributeOnModel']
// Finally in case you are making a dropdown, you can your data to the array that
// will be passed to Select field.
$options = Arr::add($options, "value", "label");
}
// finally return the fields with custom filled Dropdown
return [
// key will be used as key to get the field value in handle()
Select::make('Label', 'key')->options($options),
];
}
For my use case, I ended up using two Action Classes (one to use while registering action from Lens, another one for Action registration from Resource Class because I was generating a custom dropdown.
Nova does some weird stuff and I was getting an invalid argument for foreach while executing action from Resource.
public function fields(Request $request)
{
$budgetProject = BudgetProject::where('budget_id',$request->budget_id)->get();
dd($budgetProject);
foreach($budgetProject as $bp){
$projectArray[$bp->project_id] = $bp->project->name;
}
return [
Select::make('Project','project_id')->options($projectArray),
];
}

Laravel - How to get a foreign key and pass it to controller

I'm creating a survey and in order to create a question I need to get the survey_id from the survey table and place in it a create a question view in a hidden field in a form and pass it to the question controller.
Since you passe survey_id all you need to the do is use the right model (Question) in the store method, everything else same correct.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$input = $request->all();
Question::create($input); //<==== You need to use Question insteed of Survey
return redirect('survey');
}
You can try below code,i hope this is help you:
$input = $request->all();
$survey = new Survey ();
$survey ->fill($input)->save();
if (isset($input['survey_id']) && !empty($input['survey_id'])) {
$survey_id= $input['survey_id'];
foreach ($survey_id as $index => $value) {
Question::create([
'survey_id' => $survey ->id,
'title' => $value
]);
}
}
#Rachid's comments is right, but its not the reason of the error...
The problem is the resource's route -> controler#action mapping doesn't include any param for the /create route as we can see bellow...
... and your QuestionController::create function needs the param $id.
//...
class QuestionController extends Controller
{
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create($id) // <---- Right here...
//...
So, to bind your foreign key to this resource i'll need to do something like this...
Route::resource('survey.question', 'QuestionController');
This way you'll have your routes mapped sothing like this...
GET /survey/{survey}/question survey.question.index
GET /survey/{survey}/question/create survey.question.create
Then change your template, from:
{!! Form::open(array('action' => 'QuestionController#store', 'id' => 'Question')) !!}
To:
{!! Form::open(array('action' => 'QuestionController#store', 'id' => 'Question'), ['survey' => Request::route('survey')]) !!}
Then you can use your QuestionController::create function like this...
public function create(Survey $survey)
{
return view('question.create')->with('survey', $survey);
}
You can also use parameters in resourceful routes like this:
Route::resource('survey/{survey}/question', 'QuestionController');
Then in your controller you can use:
class QuestionController extends Controller
{
// ...
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create($id)
{
// $id will be the {survey} id from the route parameter.
}
// ...
}
You could as well use route model binding to avoid the manual Survey retrival in the controller:
use App\Survey;
class QuestionController extends Controller
{
// ...
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create(Survey $survey)
{
return view('question.create', compact('survey'));
}
// ...
}
Remember to update the form action as well to include the pameter
Be Careful!
If you register the parameter in the resource like I have shown, methods as show, edit, update and destroy (that accepts the id of the resource to act on in the url) will receive two route parameters, one will be the survey id, the second one would be the question id (your resource).
Keep that in mind!

Using Resource controller in Laravel 5.6

I am Using Resource controller in Laravel 5.6. I am following this tutorial. I found here Resource controller uses the Route model binding, This means that you dont need to fetch the specified task by the id. Laravel will do it for you. $task variable which is passed into the show() method is passed to the view via compact method.
In my code I am using below code in Controller.
/**
* Display the specified resource.
*
* #param \App\sura $sura
* #return \Illuminate\Http\Response
*/
public function show(Sura $sura)
{
return $sura;
}
Here I am getting the Whole Sura object not the id.
Why I am getting the whole object not the id ? Where is the issue ?
https://laravel.com/docs/5.6/routing#route-model-binding
When dependency inject model
public function show(Sura $sura)
{
return $sura; // it is instance of Sura
}
For get id use this
public function show($suraId)
{
dd($suraId);// return integer number
}

Laravel PHP Traits with models

I have a PHP trait that I will use in any model that can do a certain set of actions. For example one of these actions is completion where completed_at is marked with a timestamp.
The trait method is:
/**
* #return $this
* #throws Exception
*/
public function markCompleted(){
if($this->canDoAction('complete')){
$this->completed_at = Carbon::now();
return $this;
}
}
In my controller I am calling this on a model that can do this action like below.
$app->markCompleted()->save();
The $app when I view its contents it is not null.
Running this command returns an error like
local.ERROR: Call to a member function save() on null
Wouldn't $this represent the model that uses this trait?
Another variation on what The Alpha said.
/**
* #return $this
* #throws Exception
*/
public function markCompleted(){
if($this->canDoAction('complete')){
$this->completed_at = Carbon::now();
}
return $this;
}
This way you always return a model, and you can chain other functions before the save is performed if you needed.
If the condition doesn't meet then null will be returned, so instead of calling the save separately, do that inside that method, for example:
public function markCompleted()
{
if ($this->canDoAction('complete')) {
$this->completed_at = Carbon::now();
return $this->save(); // true/false
}
}
Then use it like:
$app->markCompleted();
The way way, you coded, the save method will be called even if the condition doesn't match and that's a side effect.

In Laravel why am I getting a No query results for model [TodoList]?

I'm having problems with my routes in laravel when I go to "/todos and /todos/id " everything works perfectly but when I try using the "/todos/create" I get a No query results for model [TodoList]
I'm new to this please help me... i really dont want to give up because i really love this mvc
here's my routes
Route::get('/', 'TodoListController#index');
Route::get('todos', 'TodoListController#index');
Route::get('/todos/{id}', 'TodoListController#show');
Route::get('db', function() {
$result = DB::table('todo_lists')->where('name', 'Your List')->first();
return $result->name;
});
Route::resource('todos', 'TodoListController');
model
<?php
class TodoList extends Eloquent {}
Controller
public function index()
{
$todo_lists = TodoList::all();
return View::make('todos.index')->with('todo_lists', $todo_lists);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$list = new TodoList();
$list->name = "another list";
$list->save();
return "I am here by accident";
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$list = TodoList::findOrFail($id);
return View::make('todos.show')->withList($list);
}
my views
#extends('layouts.main') #section('content')
<h1>All todo list</h1>
<ul>
#foreach ($todo_lists as $list)
<li>{{{ $list->name }}}</li>
#endforeach
</ul>
#stop
The issue is with the explicit route you defined for /todos/{id}. Since this route is defined before the resource route, it is catching the route for todos/create, and treating the text create as the {id} parameter for the show method.
Delete the explicit get routes for todos and /todos/create and your issue will be fixed. Both of these routes are handled by the resource route.
Your model doesn't have the fillable attribute. Try adding
protected $fillable = [
'name'
];
You also need to define a table
protected $table = 'todolist';
Extra
You're also manually adding some routes that are already defined by your resource controller like your show route.
You are also storing models in your create method. You should be showing a form in your create method and storing results in your store method.

Categories