I am building an application that uses the repository pattern. Now what I have done is reuse functionality but I have hit a bit of a hiccup. I have one view that shows several different models on it. These models are related through a one-to-one relationship (MovieBasic to MovieDetail) and one-to-many relationship (MovieBasic to MoviePersonnel). The issue I am having is that I have two different request to validate my forms. They are MovieBasicRequest, which validates my movie's basic information (Title, synopsis) and MovieDetailRequest, which validates my movie's detail information (price, screen type, runtime, etc). So to distinguish between which request to use I have added a parameter to my url as follows:
movie_basic.blade.php
<?php $params = ['id' => $movie->id, 'type' => 'movie_basic']; ?>
<h4>Movie Baiscs <span class="pull-right">Edit</span></h4>
<hr>
<table class="table table-bordered">
<tbody>
<tr>
<td>{{ $movie->movie_title}}</td>
</tr>
<tr>
<td>{{ $movie->movie_synopsis }}</td>
</tr>
</tbody>
</table>
I know that using the <?php ?> tags is not best practice but I will clean that up later. So because of my $params the URL will look like so
www.moviesite.dev/1/edit?movie_basic
Which will call the edit function in the controller like so
MovieController.php
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
$movie = $this->movieBasic->find($id);
return view('cms.edit', compact('movie', 'type'));
}
In this case the type does not really play a role because of the relationship between MovieBasic and MovieDetail models. However it does play a role in my update function below:
MovieController.php
/**
* Update the specified resource in storage.
*
* #param int $id, MovieBasicRequest $request
* #return Response
*/
public function update($id)
{
if(strcmp($_GET['type'], 'movie_basic') == 0)
{
$movie = $this->movieBasic->find($id);
$this->request = new MovieBasicRequest;
$this->movieBasic->update($id, $this->request);
}
elseif(strcmp($_GET['type'], 'movie_detail') == 0)
{
$movie = $this->movieBasic->find($id);
$this->request = new MovieDetailRequest;
$this->movieDetail->update($id, $this->request);
}
return redirect()->action('MovieController#show', compact('movie'));
}
Essentially what this function does is determine what is being passed in and from there call the correct request. However the way I have it now it just creates an empty array and thus validates nothing. Is there any way to uses these requests to validate information passed in? Or to validate input before I pass it to the update function of the repository?
PS. I have also tried this:
$this->movieBasic->update($id, MovieBasicRequest $request);
but I get an "Undefined variable $request" error.
You should better combine them. And you can use sometimes on your form validation for handling both where you will only validate present fields. So that your MovieRequest can be like below
class MovieRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'movie_title' => 'sometimes|required|min:3',
'price' => 'sometimes|required|integer'
// and so on
];
}
}
So you can update your controller as below and use for the both pages. For instance, if the price is not set within the request, then it will skip validating it, but if it's present and empty, then it will throw an error message as it's required.
public function update($id, MovieRequest $request)
{
$movie = $this->Movie->find($id);
$movie->fill($request->all());
$movie->save();
return redirect()->action('MovieController#show', compact('movie'));
}
Related
I am debugging a piece of code one of my team members has submitted, I am not too familiar with Laravel so bear with me if I've missed anything, but basically the form will submit and be redirected to the correct update controller, but when I try to get the result of one of the changed fields in the form, it is just blank.
Index Form Data
<form method="POST" action= "{{ route('apparatus_codes.update' , $apparatusCode->id )}}" class="is-readonly" >
#csrf
#method('PUT')
<tr id="table{{ $apparatusCode->id}}" data-target=".table{{ $apparatusCode->id}}">
<td class="main-bg"> {{ $apparatusCode->id}} </td>
<td class="data main-bg"><input name ="rent" id="rent" value = "{{ $apparatusCode->rent}}"/></td>
<input type="submit" id="save-button" class="save"><img class="mb-1 duration-300 ml-4 inset-0 h-6 w-6" src="/../../img/save-icon.svg" alt="save icon">
</form>
Controller
public function update(ApparatusCodesRequest $request, $id)
{
// find selected apparatus code details
$apparatusCodes = ApparatusCodes::find($id);
$test = $request->input('rent');
echo "TEST".$test;
}
If I echo the $apparatusCodes variable, this will display the correct data from the form on page load. However, I have an input field on the rent field, and the changed value for this field will not come through onto the controller. Also if I echo $request->all or dd($request->all()) there will be no data coming through. I can't tell why nothing is coming through when my form looks to be similar to others I have seen?
There are no errors appearing, it is reaching the intended controller ok it seems, but just not able to retrieve any of the inputted data. Any help would be great. Thanks.
Routes file
Route::post('/apparatus_codes/{id}', [App\Http\Controllers\ApparatusCodesController::class, 'update'] )->name('apparatus_codes.update');
ApparatusCodesRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Http\Request;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\ApparatusCodes;
class ApparatusCodesRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
// initalising so that memo rule doesn't throw error when creating new apparatus code
$stored_memo = '';
// this is returning id of current apparatusCode
$apparatusCodes_id = $this->route('apparatusCodes');
// this request is used for both create, and update
// we don't want to check for a stored memo when validating a create request
if(!$apparatusCodes_id == null){
// getting the current memo for grantor
// used to check that memo has been updated
$stored_memo = ApparatusCodes::where('id', $apparatusCodes_id)->pluck('memo')->firstOrFail();
}
return [
];
}
public function messages()
{
return [
'memo.not_in' => 'An update to the memo field is required'
];
}
}
I have a notes model. Which has a polymorphic 'noteable' method that ideally anything can use. Probably up to 5 different models such as Customers, Staff, Users etc can use.
I'm looking for the best possible solution for creating the note against these, as dynamically as possible.
At the moment, i'm adding on a query string in the routes. I.e. when viewing a customer there's an "Add Note" button like so:
route('note.create', ['customer_id' => $customer->id])
In my form then i'm checking for any query string's and adding them to the post request (in VueJS) which works.
Then in my controller i'm checking for each possible query string i.e.:
if($request->has('individual_id'))
{
$individual = Individual::findOrFail($request->individual_id_id);
// store against individual
// return note
}elseif($request->has('customer_id'))
{
$customer = Customer::findOrFail($request->customer_id);
// store against the customer
// return note
}
I'm pretty sure this is not the best way to do this. But, i cannot think of another way at the moment.
I'm sure someone else has come across this in the past too!
Thank you
In order to optimize your code, dont add too many if else in your code, say for example if you have tons of polymorphic relationship then will you add tons of if else ? will you ?,it will rapidly increase your code base.
Try instead the follwing tip.
when making a call to backend do a maping e.g
$identifier_map = [1,2,3,4];
// 1 for Customer
// 2 for Staff
// 3 for Users
// 4 for Individual
and so on
then make call to note controller with noteable_id and noteable_identifier
route('note.create', ['noteable_id' => $id, 'noteable_identifier' => $identifier_map[0]])
then on backend in your controller you can do something like
if($request->has('noteable_id') && $request->has('noteable_identifier'))
{
$noteables = [ 'Customers', 'Staff', 'Users','Individual']; // mapper for models,add more models.
$noteable_model = app('App\\'.$noteables[$request->noteable_identifier]);
$noteable_model::findOrFail($request->noteable_id);
}
so with these lines of code your can handle tons of polymorphic relationship.
Not sure about the best way but I have a similar scenario to yours and this is the code that I use.
my form actions looks like this
action="{{ route('notes.store', ['model' => 'Customer', 'id' => $customer->id]) }}"
action="{{ route('notes.store', ['model' => 'User', 'id' => $user->id]) }}"
etc..
And my controller looks this
public function store(Request $request)
{
// Build up the model string
$model = '\App\Models\\'.$request->model;
// Get the requester id
$id = $request->id;
if ($id) {
// get the parent
$parent = $model::find($id);
// validate the data and create the note
$parent->notes()->create($this->validatedData());
// redirect back to the requester
return Redirect::back()->withErrors(['msg', 'message']);
} else {
// validate the data and create the note without parent association
Note::create($this->validatedData());
// Redirect to index view
return redirect()->route('notes.index');
}
}
protected function validatedData()
{
// validate form fields
return request()->validate([
'name' => 'required|string',
'body' => 'required|min:3',
]);
}
The scenario as I understand is:
-You submit noteable_id from the create-form
-You want to remove if statements on the store function.
You could do that by sending another key in the request FROM the create_form "noteable_type". So, your store route will be
route('note.store',['noteableClass'=>'App\User','id'=>$user->id])
And on the Notes Controller:
public function store(Request $request)
{
return Note::storeData($request->noteable_type,$request->id);
}
Your Note model will look like this:
class Note extends Model
{
public function noteable()
{
return $this->morphTo();
}
public static function storeData($noteableClass,$id){
$noteableObject = $noteableClass::find($id);
$noteableObject->notes()->create([
'note' => 'test note'
]);
return $noteableObject->notes;
}
}
This works for get method on store. For post, form submission will work.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Requests\NoteStoreRequest $request
* #return \Illuminate\Http\Response
*/
public function store(NoteStoreRequest $request) {
// REF: NoteStoreRequest does the validation
// TODO: Customize this suffix on your own
$suffix = '_id';
/**
* Resolve model class name.
*
* #param string $name
* #return string
*/
function modelNameResolver(string $name) {
// TODO: Customize this function on your own
return 'App\\Models\\'.Str::ucfirst($name);
}
foreach ($request->all() as $key => $value) {
if (Str::endsWith($key, $suffix)) {
$class = modelNameResolver(Str::beforeLast($key, $suffix));
$noteable = $class::findOrFail($value);
return $noteable->notes()->create($request->validated());
}
}
// TODO: Customize this exception response
throw new InternalServerException;
}
In my Laravel application I have a sign up process in which users must select a category they fall under, each category is in it's own form.
In each of these forms there are a set of checkboxes and users must tick at least one, otherwise validation should fail, I've been doing some reading and found two great, similar questions:
Handling multiple forms on a single page
Laravel request validation with multiple forms on the same page
At the moment I'm feeding all 3 forms to the same method:
/**
* Store a user's selected investor type and progress onto next stage
*
* #param Request $request
* #return void
*/
public function storeInvestorType(Request $request)
{
$user = auth()->user();
$user->investor_type = $request->get('investor_type');
$user->declaration_date = Carbon::now();
$user->save();
Log::info("{$user->log_reference} has declared that they are a '{$user->investor_type}' investor.");
return redirect()->route('user.member-type');
}
Which literally just updates a column in a database.
Would it be cleaner to have 3 separate methods or just to name each form?
An update
I have added a name="something" to each submit button so that I can do something like this in the controller:
/**
* Store a user's selected investor type and progress onto next stage
*
* #param Request $request
* #return void
*/
public function storeInvestorType(Request $request)
{
$user = auth()->user();
if ($request->has('high_net_worth')){
if(!$request->has('high_net_worth_criteria')){
return redirect()->back()->withErrors('Please tick at least one criteria that specifies you are a High Net Worth investor');
} else{
$investor_type = "High Net Worth";
}
} elseif ($request->has('self_certified_sophisticated')) {
if (!$request->has('self_certified_sophisticated_criteria')) {
return redirect()->back()->withErrors('Please tick at least one criteria that specifies you are a Self-Certified Sophisticated investor');
} else {
$investor_type = "Self-Certified Sophisticated";
}
} elseif ($request->has('other')) {
$investor_type = "Other";
}
$user->investor_type = $investor_type;
$user->declaration_date = Carbon::now();
$user->save();
Log::info("{$user->log_reference} has declared that they are a '{$user->investor_type}' investor.");
return redirect()->route('user.member-type');
}
As the title states, I'm getting an odd error in Laravel 5. I'm new to Laravel, and this week I dived into Jobs/Queues. I've gotten an "Undefined Variable: $errors" error in the past, and that one I was able to understand and fix. But now, I can't seem to get past this one. To my knowledge, everything looks fine. The following breakdown will (hopefully) give you an idea of what I'm doing/where the error happens:
class PostFormFields extends Job implements SelfHandling
{
use InteractsWithQueue, SerializesModels;
/**
* The id (if any) of the Post row
*/
protected $id;
/**
* List of fields and default value for each field
*/
protected $fieldList = [
'title' => '',
'subtitle' => '',
'page_image' => '',
'content' => '',
'meta_description' => '',
'is_draft' => '8',
'publish_date' => '',
'publish_time' => '',
'layout' => 'blog.layouts.post',
'tags' => [],
];
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($id = null)
{
$this->id = $id;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$fields = $this->fieldList;
if($this->id)
{
$fields = $this->fieldsFromModel($this->id, $fields);
} else {
$when = Carbon::now()->addHour();
$fields['publish_date'] = $when->format('M-j-Y');
$fields['publish_time'] = $when->format('g:i A');
}
/**
* Populate with old values, if they exist
* #var [type]
*/
foreach ($fields as $fieldName => $fieldValue)
{
$fields[$fieldName] = old($fieldName, $fieldValue);
}
$fields = array_merge($fields, ['allTags' => Tag::lists('tag')->all()]);
return $fields;
}
Above is the code inside the handler function of my Job class, the file it sits in is called PostFormFields.php. It's job, essentially, is just to return an array filled with all the values pertaining to a post, based on the Post Model and what's in the database that pertains to that specific Post ('title','content',etc) if a user's entered them in the past
public function create()
{
$data = $this->dispatch(new PostFormFields());
$data['title'] = 'testing';
var_dump($data);
return view('admin.post.create', $data);
}
Above is the code inside my PostController class, in the create() method. As you can tell, I'm using a resource controller for my Post Controller. It dispatches the PostFormFields Job and stores all the returned data in an array $data. However, since the create() method will be used to create a new post, only the keys should be returned, with values set to their default value ''.
This works. As you can see, i run a 'var_dump()' on the variable $data to see what, if anything, is returned. I then pass the $data array to the create View. This is where the error comes up.
Laravel "Undefined Varieble" Error
Above is a picture of the error I get when I try to access the /create route. It's clear that the $data does have the $title variable defined, as well as all the other keys in the array. Why am I getting an "Undefined Variable" array when I clearly have it defined by the time it's sent to the create View?
The line of code is says the error is in is the following:
<input type="text" class="radius" name="title" id="title" value="{{ $title }}">
You have to pass that array to view via compact function of laravel. So that you can use it in view as you want.
Please check about compact here - https://laracasts.com/discuss/channels/general-discussion/phps-compact-pros-and-cons?page=1
public function create()
{
$data = $this->dispatch(new PostFormFields());
$data['title'] = 'testing';
var_dump($data);
return view('admin.post.create', compact('data'));
}
I have the 2 simple tables below:
CUSTOMERS
id, email
CLAIMS
id, customer_id(fk), description
I created the related models (Customers.php and Claims.php) and set-up relationships: hasOne() and belongsTo().
I also have my related RESTful controllers ready: CustomersController.php and ClaimsController.php.
What would be the best solution if I need to create/update records in both tables by submitting one form? Create one general controller? Mix models?
I have been searching in Laravel docs and on Google and still have no idea how to achieve this.
Customer model
public function claims(){
return $this->hasMany('App\Claims');
}
Claims model
public function customer(){
return $this->belongsTo('App\Customer');
}
Now in controller u need to send request in store action
Something like this
class CreateCustomerClaim extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required',
'description'=> 'required'
];
}
}
Now in store action send your request, grab data from request and insert it in db
public function store(CreateCustomerClainsRextends $request)
{
//example
$customer= new Customer($request->all());
Auth::user()->claims()->save($customer);
}
if u need to update use same request in update function, when u grab data from request just use update. Here is example where i update 3 different tables from one request
public function update($id,ArtikalUpdateRequest $request)
{
$article = Artikal::findOrFail($id);
if($article !== null){
$article->update($request->all());
\DB::table('artikal_podkategorija')
->where('artikal_id', $article->id)
->update(array('podkategorija_id' => $request['podkategorija']));
\DB::table('arikalslike')
->where('artikal_id', $article->id)
->update(array('NazivSlike' => $request['NazivSlike']));
$slika = \DB::table('arikalslike')
->where('artikal_id', $article->id)->first();
$image = Request::file('image');
//dd($image);
if($image != null){
$destinationPath = 'uploads/artiklislike/';
$thumb = $slika->SifraSlike;
$fileName = $thumb;
$nazivthumb = $slika->NazivThumb;
$slika->NazivSlike = $request['NazivSlike'];
$slika->NazivThumb = $nazivthumb;
$slika->SifraSlike = $fileName;
$slika->artikal_id = $article->id;
//Snima sliku
$img = Image::make(Input::file('image'));
$destinationPath = $destinationPath.$fileName;
Image::make($img)->save($destinationPath);
// Snima sliku u manjem formatu thumb
$destinationPath = 'uploads/artiklislike/';
$img = Image::make(Input::file('image'));
$destinationPath = $destinationPath.$nazivthumb;
Image::make($img)->resize(300, 200)->save($destinationPath);
}
}
return redirect('artikli')->with(['flash_message' => 'Uspiješno ste obrisali artikal!']);
}