Laravel all input fields are required - validation

I have about 50 input fields and they are all required.
Is there a easy way to check if they are all set?
So i don't have to manually put every field in my model with a validation rule of "required"?

I know this is late, but I wrote a simple function to solve this problem for myself
foreach ($request->except('_token') as $data => $value) {
$valids[$data] = "required";
}
$request->validate($valids);
Good luck!

You can use the code below. This worked for me to make all request fields required.
$request->validate([
'*' => 'required',
]);

It is currently not possible to avoid referencing each of your 50 fields inside your rules() method, as the returned array goes through this one :
/**
* Explode the rules into an array of rules.
*
* #param string|array $rules
* #return array
*/
protected function explodeRules($rules)
{
foreach ($rules as $key => &$rule)
{
$rule = (is_string($rule)) ? explode('|', $rule) : $rule;
}
return $rules;
}
You can see it in the Validator's constructor here : https://github.com/illuminate/validation/blob/master/Validator.php

foreach($req->except('_token') as $data=>$value){
$valids[$data] = "required";
}
$validator= validator($req->all(),$valids);
if ($validator->fails()) {
foreach($validator->messages()->getMessages() as $key=>$val){
$response["$key"]=$val;
}
return response()->json([
'success' => 0,
'errors' =>$response,
]);

also you can use validator from Facades like this:
try {
foreach ($request->except('_token') as $data => $value) {
$valids[$data] = "required";
}
$validator = validator($request->all(), $valids);
if ($validator->fails()) {
foreach ($validator->messages()->getMessages() as $key => $val) {
$response["$key"] = $val;
}
return redirect()->back()
->withErrors($response)
->withInput();
}
}catch(\Exception $e){
die($e->getMessage()) ;
}

Related

Observer not running when creating in database

Hello I have this method that mass create student sections
Enrollment controller method this code works fine but it doesn't get in my studentSectionObserver. Although it's getting saved one by one with for loop.
public function setStudentsSection(Request $request)
{
$enrollments = Enrollment::whereIn('student_id', $request->students)->where('session_id', $request->session_id)->get();
$program_section = ProgramSection::withCount('students')->find($request->program_section_id);
if(($program_section->students_count + count($enrollments)) <= $program_section->max_students) {
$new_student_sections = array();
foreach($enrollments as $enrollment) {
$data = [
'student_id' => $enrollment->student_id,
'enrollment_id' => $enrollment->id,
'section_id' => $request->program_section_id,
'created_at' => Carbon::now()
];
array_push($new_student_sections, $data);
}
return StudentSection::insert($new_student_sections);
}
return response()->json(['errors' => ['message' => 'Selected Section is full.']], 405);
}
Then i output this activity with studentSectionObserver and added log::info but it doesn't log anything
public function created(StudentSection $student_section)
{
Log::info('test');
$student = $student_section->student()->get()->first();
$section = $student_section->section()->get()->first();
Logger::createLog("Assigned " . $student->first_name . " " . $student->last_name . " '" . $section->section->name . "'");
}
I know this observer gets triggered cause i tested it with this method whenever i add section the studentSectionObserver triggers Logger.
public function enrollStudent(EnrollmentRequest $request)
{
$check_if_exist = Enrollment::where('student_id', $request->student_id)->where('session_id', $request->session_id)->first();
if (!$check_if_exist) {
$program_section = ProgramSection::withCount('students')->find($request->section_id);
if($program_section) {
if($program_section->students_count < $program_section->max_students) {
$enrollment = Enrollment::create($request->all());
$section_data = ['student_id' => $request->student_id, 'section_id' => $request->section_id, 'enrollment_id' => $enrollment->id];
$section = StudentSection::create($section_data);
return response()->json($enrollment, 200);
}
return response()->json(['errors' => ['message' => 'Selected Section is full.']], 405);
}
$enrollment = Enrollment::create($request->all());
return response()->json($enrollment, 200);
}
return response()->json(['errors' => ['message' => 'Student is already enrolled in this session.']], 405);
}
Any help would be greatly appreciated.
As you have figured out the answer on your own.
The reason for using create method is because it triggers the event on the model. Same goes for update method.
Following is the update method under the hood:
/**
* Update the model in the database.
*
* #param array $attributes
* #param array $options
* #return bool
*/
public function update(array $attributes = [], array $options = [])
{
if (! $this->exists) {
return false;
}
return $this->fill($attributes)->save($options);
}
And the save method has these lines of code:
if ($this->fireModelEvent('saving') === false) {
return false;
}
Similarly, create method works. That's why insert doesn't trigger the event on model and you had to use create method.
Turns out i just need to use create method.
public function setStudentsSection(Request $request)
{
$enrollments = Enrollment::whereIn('student_id', $request->students)->where('session_id', $request->session_id)->get();
$program_section = ProgramSection::withCount('students')->find($request->program_section_id);
if(($program_section->students_count + count($enrollments)) <= $program_section->max_students) {
foreach($enrollments as $enrollment) {
$response = StudentSection::create([
'student_id' => $enrollment->student_id,
'enrollment_id' => $enrollment->id,
'section_id' => $request->program_section_id,
'created_at' => Carbon::now()
]);
return $response;
}
}
return response()->json(['errors' => ['message' => 'Selected Section is full.']], 405);
}
Laravel observer doesn't work on bulk objects, it only works with single object.
So when you use create() function it will trigger the observer.

Laravel - How to update Input Array without deleting Sales Detail

In my Laravel-8 project, I have this controller for Input Field Array Update.
Controller:
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
SaleDetail::where('sale_id', $sale->id)->delete();
foreach ($data['invoiceItems'] as $item) {
$details = [
'sale_id' => $sale->id,
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$saleDetail = new SaleDetail($details );
$saleDetail->save();
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
In the form, the user can add more Sales Detail or remove.
Some of the SaleDetail fields are being used somewhere else.
Is there a way to update the input field array without deleting the SaleDetail as shown in what I did here:
SaleDetail::where('sale_id', $sale->id)->delete();
Thanks
I've tried to restructure your code so that's easier to edit. I've left some comments. I can really recommend refactoring.guru. There you will find many ways to improve your code so that it is more extensible, maintainable and testable. If you have any questions, please feel free to ask.
class Sale extends Model
{
// Use a relationship instead of building your own query
public function details() {
return $this->hasMany(SaleDetail::class);
}
}
class SaleDetail extends Model
{
// Use a computed property instead of manually calculating total price
// You can access it with $saleDetail->totalPrice
public function getTotalPriceAttribute() {
return $this->price * $this->quantity;
}
}
class UpdateSaleRequest extends Request
{
public function authorize() {
return true;
}
protected function prepareForValidation() {
$this->merge([
// Create a Carbon instance by string
'date' => Carbon::make($this->date)
]);
}
public function rules() {
// Your validation rules
// Please also validate your invoice items!
// See https://laravel.com/docs/8.x/validation#validating-arrays
}
}
// We let Laravel solve the sale by dependency injection
// You have to rename the variable name in ihr web.php
public function update(UpdateSaleRequest $request, Sale $sale)
{
// At this point, all inputs are validated!
// See https://laravel.com/docs/8.x/validation#creating-form-requests
$sale->update($request->validated());
// Please ensure, that all properties have the same name
// In your current implementation you have price = cost, be consistent!
foreach($request->input('invoiceItems') as $invoiceItem) {
// How we can consider that a detail is already created?
// I assume that each item_id will only occur once, otherwise you'll
// place the id of each detail in your update form (e.g. in a hidden input)
$candidate = $sale->details()
->where('item_id', $properties['item_id'])
->first();
if($candidate) {
$candidate->update($properties);
} else {
$sale->details()->create($properties);
}
}
// A JWT-Exception should not be necessary, since your authentication
// will be handled by a middleware.
return response()->json($sale);
}
I have not tested the code, few adjustments may be needed.
Laravel has a method called updateOrCreate as follow
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
$instance->fill($values)->save();
});
}
That means you could do some thing like
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
foreach ($data['invoiceItems'] as $item) {
$details = [
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$sale->saleDetail()->updateOrCreate([
'sale_id' => $sale->id
], $details);
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
I would encourage you to refactor and clean up your code.You can also read more about it here https://laravel.com/docs/8.x/eloquent#upserts

GuzzleHttp\Exception\InvalidArgumentException IDN conversion failed

after i added product data into database, this error page show me instead of routing to product page.
But, data is successful inserted.
ProductsController.php
public function store(Request $request)
{
$slug = $this->getSlug($request);
$dataType = Voyager::model('DataType')->where('slug', '=', $slug)->first();
// Check permission
$this->authorize('add', app($dataType->model_name));
// Validate fields with ajax
$val = $this->validateBread($request->all(), $dataType->addRows);
if ($val->fails()) {
return response()->json(['errors' => $val->messages()]);
}
if (!$request->ajax()) {
$requestNew = $request;
$requestNew['price'] = $request->price * 100;
$data = $this->insertUpdateData($requestNew, $slug, $dataType->addRows, new $dataType->model_name());
event(new BreadDataAdded($dataType, $data));
$this->updateProductCategories($request, $data->id);
return redirect()
->route("voyager.{$dataType->slug}.index")
->with([
'message' => __('voyager.generic.successfully_added_new')." {$dataType->display_name_singular}",
'alert-type' => 'success',
]);
}
}
Plz help me

Figure what fields `save()` acted on if any (detecting changes)

Doing ->save() and ->update() only updates when changes made, I think, is this true?
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
I typically update like this:
public function update(Requests\UpdatePetRequest $request, Pet $pet)
{
$pet->update($request->all());
return $pet;
}
I want to figure from ->save and from ->update if what field names were updated? Along with what was the old value and the new value.
I currently manually do this like this:
public function update(Requests\UpdatePetRequest $request, Pet $pet)
{
$changes = [];
if ($request->exists('name') && $request->name != $pet->name) {
$changes['name'] = array([
'old' => $pet->name,
'new' => $request->name
]);
}
if ($request->exists('avatar') && $request->avatar != $pet->avatar) {
$changes['avatar'] = array([
'old' => $pet->avatar,
'new' => $request->avatar
]);
}
if (!count($changes)) {
return response()->json(['error'=>'No properties changed'], 422);
}
$pet->update($request->all());
$body = json_encode($changes);
$message = new Message(['body' => $body, 'kind' => 'PET_UPDATE']);
}
Is there an automated way to do this?
You can't do that with update(), but you can use getDirty() before save():
$model = Model::find($id);
$model->fill($request->all());
$cahnges = $model->getDirty();
$model->save();
getDirty() will return you an array with changed columns only, for example:
['name' => 'New Name', 'address' => 'New Street, 12']

Create Relationship inside the create function

I have a model that has a one to many relationship to the versions of the description.
In my Controller
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
]);
$tag->update([
'content' => $request->get('description')
]);
In my Model:
public function setContentAttribute(string $value)
{
$this->versions()->create([
'user_id' => \Auth::id(),
'value' => $value
]);
}
So I can't put content directly as an attribute in the create method because there is no Model right now.
But is it possible to overwrite the create Method?
When I try to overwrite something like this in my Model it will do an infinity loop
public static function create($attr) {
return parent::create($attr);
}
So my question is if it is possible to have something like this:
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
'content' => $request->get('content')
]);
and in the Model:
public static function create($attr) {
$value = $attr['content'];
$attr['content'] = null;
$object = parent::create($attr);
$object->content = $value;
$object->save();
return $object;
}
Update
I didn't overwrite the create method but called it customCreate. So there is no infinity loop anymore and I can pass all variables to the customCreate function that handles the relationships for me.
Solution
After reading the changes from 5.3 to 5.4 it turns out that the create method was moved so you don't have to call parent::create() anymore.
The final solution is:
public static function create($attr) {
$content = $attr['content'];
unset($attr['content']);
$element = static::query()->create($attr);
$element->content = $content;
$element->save();
return $element;
}
I don't see why not and you could probably implement a more general approach? Eg. checking if set{property}Attribute() method exists, if it does - use it to assign a value, if it doesn't - use mass assigning.
Something like:
public static function create($attr) {
$indirect = collect($attr)->filter(function($value, $property) {
return method_exists(self::class, 'set' . camel_case($property) . 'Attribute');
});
$entity = parent::create(array_diff_key($attr, $indirect->toArray()));
$indirect->each(function($value, $property) use ($entity) {
$entity->{$property} = $value;
});
$entity->save();
return $entity;
}
I haven't really tested it but it should work. I use something like this in one of my Symfony apps.

Resources