Validation: how to set a field that is not required to 'null' when input is empty - laravel

I have a validation rule that looks like this:
$rules = ['target' => 'numeric'];
It's not a required field. If a value is not specified in the input (i.e. Input::get('target') == ''), I want the field to be set to NULL in the database.
Currently the above rule passes, and in the absence of a numeric input, it gets set to 0 in the database.
What's the best solution?

You can set field as null in Laravel simply by assigning null value to the appropriate model attribute before calling save().
if(! Input::get('target') ){
$eloquent_model->target = null;
}
$eloquent_model->save();
But if you want to insert null values in more than one model, you can create base model and inherit it by all other models.
class BaseModel extends Eloquent {
public static function boot()
{
parent::boot();
static::creating(function($model) {
static::setNullWhenEmpty($model);
return true;
});
}
private static function setNullWhenEmpty($model)
{
foreach ($model->toArray() as $name => $value) {
if (empty($value)) {
$model->{$name} = null;
}
}
}
}
Now all empty fields will be set to null automatically and you don't have to check before save.
Reference.

In this case i like to use mutators :
public function setTargetAttribute($target){
$this->attributes['target'] = $target ?: null;
}

I don't think it's an issue of laravel, you must add tag mysql...
Alter the table to add Null as default value.. Currently if you pass blank value, it'll add 0 because by default it is not NULL and shows
Warning: #1366 Incorrect integer value: '' for column 'target' at row 1.
Here is the query to alter..
ALTER TABLE `table` CHANGE `target` `target` INT( 11 ) NULL
After running above query, target will accept NULL value instead of 0!!

My solution was this (Laravel 5.2):
//Model.php
public function nullIfBlank($field) {
return trim($field) !== '' ? $field : null;
}
// your custom model that extends Model.php
public function setTargetAttribute($target) {
$this->attributes['target'] = $this->nullIfBlank($target);
}
Pay attention to naming your setter (mutator) exactly as above, lowerCamelCase and magic will happen.

Related

Using Model name from Variable

I have a variable which holds the model name like so
$fooTableName = 'foo_defs';
$fooModel = 'FooDefs';
Now I would like to insert in the DB using that model like so
$fooModel::insert([..foo..array...]);
Throws an error
"message": "Parse error: syntax error, unexpected '$fooModel' (T_VARIABLE), expecting identifier (T_STRING)",
Is it possible to do something like that? or will I be forced to use
DB::table('fooTableName')->insert([...foo...array...]);
If I do it in the latter way, the timestamps in the table are wrong. The created_at column is null and the updated_at has the value
EDIT 1
$model = CustomHelper::getNameSpace($this->tableNames[$i]);
// $model => /var/www/html/erp/app/Models/sales/InvoiceDefs
$model::insert($this->tableCollections[$this->tableNames[$i]]);
Most of them said that, it was namespace issue, so I have corrected it, but still it is throw error like
"message": "Class '/var/www/html/erp/app/Models/sales/InvoiceDefs' not
found",
What you are doing wrong is using model name as string, you need to refactor your code as like below :
$fooModel = 'App\Models\FooDefs';
I have a same situation before and i have created the function to do this
function convertVariableToModelName($modelName='',$nameSpace='')
{
//if the given name space iin array the implode to string with \\
if (is_array($nameSpace))
{
$nameSpace = implode('\\', $nameSpace);
}
//by default laravel ships with name space App so while is $nameSpace is not passed considering the
// model namespace as App
if (empty($nameSpace) || is_null($nameSpace) || $nameSpace === "")
{
$modelNameWithNameSpace = "App".'\\'.$modelName;
}
//if you are using custom name space such as App\Models\Base\Country.php
//$namespce must be ['App','Models','Base']
if (is_array($nameSpace))
{
$modelNameWithNameSpace = $nameSpace.'\\'.$modelName;
}
//if you are passing Such as App in name space
elseif (!is_array($nameSpace) && !empty($nameSpace) && !is_null($nameSpace) && $nameSpace !== "")
{
$modelNameWithNameSpace = $nameSpace.'\\'.$modelName;
}
//if the class exist with current namespace convert to container instance.
if (class_exists($modelNameWithNameSpace))
{
// $currentModelWithNameSpace = Container::getInstance()->make($modelNameWithNameSpace);
// use Illuminate\Container\Container;
$currentModelWithNameSpace = app($modelNameWithNameSpace);
}
//else throw the class not found exception
else
{
throw new \Exception("Unable to find Model : $modelName With NameSpace $nameSpace", E_USER_ERROR);
}
return $currentModelWithNameSpace;
}
How To user it:
Arguments
First Argument => Name of the Model
Second Argument => Namespcce of the Model
For Example we have the model name as Post
$postModel = convertVariableToModelName('Post');
dd($postModel::all());
Will returns all the values in the posts table
But in Some Situation You Model Will in the
Custom Namespace such as App\Models\Admin\User
So this function is created to overcome that
$userModel = convertVariableToModelName('User',['App','Models','Admin']);
dd($userModel::all());
You are feel free to customize the function
Hope it helps
Try the below one,
$fooModel = new FooDefs();
and then you can do the following also,
$fooModel->column1 = $value1;
$fooModel->column2 = $value2;
$fooModel->column2 = $value2;
$fooModel->save();
or
$fooModel->save([
'column1' => $value1,
'column2' => $value2,
'column3' => $value3,
])
Edited answer
$path = 'my\project\path\to\Models';
$fooModel = app($path.'\FooDefs');
$fooModel::save([
'column1' => $value1,
'column2' => $value2,
'column3' => $value3,
])
dd($fooModel ::all());
Try my edited answer.
When you have the name of class stored as string you can call method on that class by using the php method call_user_func_array like this
$class_name = "FooDefs";
call_user_func_array(array($class_name, "insert"), $data);
$data is an Array of data which will be past to the called function as arguments.
Just for simple advice It's will be Good if you save in the $class_name variable the FQN Fully Qualified Name of the class which is the __NAMESPACE__ follow by the name of the class. For sample purpose FQN look like Illuminate\Support\Facades\DB which you can get when you save you use User::class I presume you have some User model. That will return the Fully Qualified Name of the User model will will be App\User in case of Laravel.
$requests = $post['request'] // posting the data from view page
$models = "app\models".'\\'.$requests //geting the model
$model = $models::findOne($referenceId) //fetching value from database

Laravel Mutators and Accessors

I have created mutator date function in model to convert the created_at date into human readable time using diffForHumans().
I have done the following
public function setDateAttribute($value){
return \Carbon\Carbon::parse($value)->diffForHumans();
}
It works fine but it affects in all. it is possible to apply this mutator only on the specified function of the controller rather than all function
A small logic in the mutator would do the job:-
public function setDateAttribute($value){
if ( request()->path() === "/yourURL/To/IndexMethod"){
return $this->attributes['date'] = \Carbon\Carbon::parse($value)->diffForHumans();
} else {
return $this->attributes['date'] = $value;
}
}
the idea is to check by the url.
getting request path helper
EDIT
Comparison using route name is better, as exact path can contain id/slugs.
public function setDateAttribute($value){
if ( \Route::currentRouteName() === "/yourRouteName/To/IndexMethod"){
return $this->attributes['date'] = \Carbon\Carbon::parse($value)->diffForHumans();
} else {
return $this->attributes['date'] = $value;
}
}
getting route name

Treating belongsTo() that returns null

I have a model (request) that returns customer object base on it's id, it's receiving the id from a different machine so I want to treat non-existent values (i.e. id that doesn't exist returning the eloquent as null).
so for:
public function customer()
{
return $this->belongsTo('App\Customer', 'site_user');
}
I've tried the following:
public function getSiteUserAttribute()
{
if (!$this->relationLoaded('customer')) {
$this->load('customer');
}
return $this->getRelation('customer') ?: $this->nullCustomer();
}
and nullCustomer() :
private function nullCustomer()
{
$nonExist = 'non-exist-customer';
$siteUser = new \Illuminate\Support\Collection;
$siteUser->first_name = $nonExist;
$siteUser->last_name = $nonExist;
$siteUser->email = $nonExist;
return $siteUser;
}
Yet Laravel is returning an error I can't really make sense of:
Undefined property: App\Request::$site_user (View: /../../index.blade.php)`
it's obviously related to getSiteUserAttribute() which extrapolates site_user, but I can't understand what's the issue.
I can isset() for every place that this relation is called, but i'm working with a smart framework, so I doubt that would be the best practice.
Just to reiterate, I'm trying ot treat null belongsTo() that wouldn't break the view.
Rewrite nullCustomer() to return App\Customer not \Illuminate\Support\Collection:
I have not tested it though.
private function nullCustomer()
{
$nonExist = 'non-exist-customer';
$siteUser = new \App\Customer;
$siteUser->first_name = $nonExist;
$siteUser->last_name = $nonExist;
$siteUser->email = $nonExist;
return $siteUser;
}
It seems site_user field and site_user attribute is mixed.
The easiest way to get rid of the confusion, rename the field as site_user_id
As Murat Tutumlu said, you can't have a site_user attribute and a getSiteUserAttribute() accessor at the same time.
You can specify default values with withDefault():
public function customer()
{
return $this->belongsTo('App\Customer', 'site_user')
->withDefault([
'first_name' => 'non-exist-customer',
'last_name' => 'non-exist-customer',
'email' => 'non-exist-customer'
]);
}

LARAVEL 5 How to store empty DateTime input to Null value instead of 0000:00:00 00:00 value

i had two input Datetime field.
User can choose to fill in either these two field.
For the empty input Datetime field, i want it to store as Null value in Database.
But currently the empty input Datetime field is store as 0000:00:00 00:00 value in Database. What code should i modified?
you can use model observers to unset the attribute before saving when it is empty. But be sure that the fields are nullable
class Flight extends Model
{
public static function boot()
{
parent::boot();
self::saving(function ($flight_model) {
if(empty($flight_model->arriveDateTime)) unset($your_model->attributes['your_date_field1'];
if(empty($flight_model->departDateTime)) unset($your_model->attributes['your_date_field2'];
});
}
}
this discussion would be a good reference
Update
to do the limitation in the controller you'll use required_without_all:foo,bar,... which as described in doc.s
The field under validation must be present and not empty only when all of the other specified fields are not present
to use it we'll add new rules like
$rules = array(
'arriveDateTime' => 'required_without_all:departDateTime',
'departDateTime' => 'required_without_all:arriveDateTime',
);
$validator = Validator::make(Input::all(), $rules);
and if you're using rules already just append this two roles to them. that's for the controller validation part.
for the view part I'll assume you've two inputs with id="arriveDateTime" and id="departDateTime" the code would be like
$(function() {
<!-- if the first input value changed disable the second input -->
$('#arriveDateTime').on('change', function() {
if ($(this).val() == '') {
$('#departDateTime').prop("disabled", false);
} else {
$('#departDateTime').prop("disabled", true);
}
});
<!-- if the second input value changed disable the first input -->
$('#departDateTime').on('change', function() {
if ($(this).val() == '') {
$('#arriveDateTime').prop("disabled", false);
} else {
$('#arriveDateTime').prop("disabled", true);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<input type="text" id="arriveDateTime" />
<input type="text" id="departDateTime" />
All you need is Eloquent Mutators.
Below a simplified example, basically from https://laravel.com/docs/5.4/eloquent-mutators#defining-a-mutator
class User extends Model
{
/* Set or mutate value of property */
public function setDateInputAttribute($value)
{
$this->attributes['date_input'] = $this->dateTimeOrNull($value);
}
/* This method should get another place, because it's not the core of this model */
private function dateTimeOrNull($value)
{
/*
Check if given datetime is valid. Yes? Return original $value
For simplicity, I use PHP's DateTime class
*/
if (DateTime::createFromFormat('Y-m-d H:i:s', $value) !== false) {
return $value;
}
/* Datetime is not valid. Return null */
return null;
}
}
Further
I suppose your datetime format is like yyyy-mm-dd hh:ii:ss
You can uses laravel mutators. Here's one for date of birth.
public function setDob($value)
{
$this->attributes['dob'] = strlen($value)? Carbon::createFromFormat('d/m/Y', $value) : null;
}

check if dates are not within range before saving in yii

how can i check if the dates posted in user's form exist in a range before saving? It's suppose to check different scenario date ranges. Below is just one of them. If it doesn't fall into range, it then posts.
is there a better way to do this?
$model = Table::model();
$criteria = new CDbCriteria;
//this is where i don't know how to get the values ($start,$end) from user before posting
$criteria->addCondition('start_date < '.$start);
$criteria->addCondition('end_date > '.$end);
You have to create custom validation function :
In rules add : this rules will work for both insert and update.
public function rules()
{
return array(
array('dateField', 'myCheckdate',),
);
}
In your custom validation function , you can apply your code to check the date range
public function myCheckdate($attribute,$params)
{
if(!$this->hasErrors())
{
if(Condition == false) // place your condition here
{
$this->addError($attribute,'The date is incorrect.');
}
}
}

Resources