In my Yii2 project, I have related models. Example: A model Customer would have an attribute address_id that relates to another model Address. In the Customer model have the exist validator that checks that the row exists in the address table.
Typically, on create or update, this validation is ignored if address = null. My problem is that sometimes FE would send the address = 0 indicating the absence of an address.
In this case, I need to not only ignore validation, but to set address = null. This can be done beforeSave of course, but I'm trying to check if there's some built-in way through which I can do this
You can use the filter validator for normalization of an input data. For example:
class Customer extends ActiveRecord
{
public function rules()
{
return [
['address_id', 'filter', 'filter' => function ($value) {
return $value == 0 ? null : $value;
}],
// other validators
];
}
}
Related
I've made accessor for formatting date on my table model,
and this is my model looks like for the accessor
class Agent Authenticatable implements MustVerifyEmail
{
protected $appends = ['created_at_formatted'];
...
public function getCreatedAtformattedAttribute()
{
return $this->created_at->format('d-m-Y');
}
...
}
there is no problem with that if I retrieve all data from the table,
but the problem comes if I only take some data, for example, I take only phone and email for specific User, and the created_at column not involved,
and, my query on controller looks like this:
public function getAgentDetail($id)
{
$agDetail = Agent::where('id','=' ,50)->get(['phone','email']);
return response()->json($agDetail);
}
and the query above gives me an error :
Symfony\Component\Debug\Exception\FatalThrowableError
Call to a member function format() on null
and I think it's because no value was given to the accessor (in this case created_at),
so I change the accessor on my model to this:
public function getCreatedAtformattedAttribute()
{
$this->created_at
? $this->created_at->format('d-m-Y')
: null;
}
it only gives a condition if it is not set, it will return a null value,
The error disappears and returns this collection :
[
{
phone: "086741111111",
email: "example#jab.com",
created_at_formatted: null
}
]
What I want is, how to ignore accessor involved when only select specify column, which has nothing to do with the accessor,
for example, I just want to get the phone and email, and not for created_at_formatted
Remove the following line and try again:
protected $appends = ['created_at_formatted'];
Acessors can be called without appending them. Also you may want to change the function name to getCreatedAtFormattedAttribute to respect Laravel's convention.
Laravel 5.5 introduces a new streamlined request validation. The idea being that:
$validData = $request->validate($rules)
will return only those fields that are present in the $rules. This is beneficial so that one can then simply User::create($validData) or User::update($validData).
However, I noticed that when I have additional data, which I do validate, but that doesn't exist in the model's (this case User) table, the create method inserts the record, but that the update method returns this error:
Column not found: 1054 Unknown column 'column_name' in 'field list'
I can of course write the relevant field lists out, or use other workaround, but I wonder why are the two methods are behaving differently?
e.g. passing
array:4 [▼
"first_name" => "Pedro"
"last_name" => "Taco"
"email" => "taco#example.org"
"operators" => array:1 [▼
0 => "1"
]
]
to User::create() inserts the record. Passing the same to User::update() returns an error.
User has $fillable = 'first_name', 'last_name', 'email'
I believe they behave the same but in update method you have some extra fields that are not in table in database, so you should verify what exactly you send and what exactly you validate to make sure there are no additional fields in $validData when you are updating user.
If you think that's not the case, please show us exactly how the validation looks like for creating and updating user.
If you want to update specific data use where
User::where('id',1)->update($validData);
If you want to update all rows try something like this
User::where('id','!=',-1)->update($validData);
OR
User::query()->update($validData);
If you are trying to use validations, you can decouple the code even further using Request validations.
Here you have a full example of applying Request validations and extracting just the necessary data to store (or update) objects:
1. Generation the request:
php artisan make:request CreateUserRequest
2. Custom the validation rules:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// you specify here a previous condition before access to the method
// can be Auth::check() for example
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
// put here your validation rules like:
'first_name' => 'required|string|max:100',
];
}
3. Modify the method in your controller:
public function store(CreateUserRequest $request)
{
// getting all the attributes
$data = $request->only('first_name', 'last_name', 'other_attribute..');
// creating the object
$user = new User;
// adding data
$user->fill($data);
// saving in database
$user->save();
return response()->json([
"message" => "The user has been created.",
"data" => $user,
], 201);
}
* Notice that the $request used as an argument is of type CreateUserRequest, so this indicates to Laravel wath rules to apply.
We have also only pull the data that we want to store (check the $request->only part), that seemed to be the root of your problem.
I have two models: Cities and Schools. As you already understand Cities can have many schools and taking this into account I have defined my model as follows:
class School extends Model
{
public $fillable = ['city_id' ,'name'];
public function city()
{
return $this->belongsTo('App\City','city_id','id');
}
}
class City extends Model
{
public $fillable = ['name'];
Public function schools()
{
return $this->hasMany('App\School', 'id','city_id');
}
}
But I have faced a pproblem when trying to validate update of a school model. I have to validate whether name of the school is unique for selected city or not. I have defined the rule like this:
$rules = array(
'name' => ['required', Rule::unique('schools')->ignore($id)],
);
$validator=Validator::make(Input::all(),$rules);
But it is not allowing to save a school with existing name in other city than selected. How should I change the rule to ensure that school names can be same if the city is different.
Thank you.
Custom rule
The best solution would be to create a custom rule for this, that accepts the field with the corresponding city name/id as a parameter.
Something like
//Calling the custom rule like this
['name' => 'required|validateSchool:yourFieldNameOfCityHere'];
Declaring the custom validation function in your service provider like this
Validator::extend('validateSchool', function ($attribute, $value, $parameters, $validator) {
$cityName = ($validator->data, $parameters[0]);
//Now check if the $value is already set for the specific city by using normal database queries and comparison
return count(City::whereName($cityName)->schools()->whereName($value)->get()) == 0
});
What does it
The custom validation rule receives the data of the field you give with the function (in the code above it's yourFieldNameOfCityHere), so it knows which city the user chose. With this information, you now can check if there is already a school with the name for the entered city.
At the DB level, it sounds like what you want is a compound uniqueness constraint across name and city_id. Eloquent seems to support passing an array of column names in model definitions. It seems like this requires custom validation, though. See Laravel 4: making a combination of values/columns unique and the custom validator at https://github.com/felixkiss/uniquewith-validator
OrganizationsController.php
public function user_index()
{
if(!is_null(Organization::find(Auth::user()->player->organization)))
$organization = Organization::find(Auth::user()->player->organization->id);
else $organization=null;
return view('organizations.user_index', [ 'organization' => $organization ]);
}
To avoid "Trying to get property of non-object" when "player" have no "organization" I used this code. But it doesnt seem very nice. There is a better way to obtain this? Maybe im wrong but with this method there is a useless query, am I right?
Table Player: id, name
Table Organization: id, name, player_id
Assuming user hasOne player, and player hasOne organization, and these relationships are setup correctly, there is no need for either Organization::find() at all. The organization attribute is going to be the loaded Organization object already, so there is no need to re-find it.
public function user_index() {
// default to null
$organization = null;
// make sure there is an authenticated user and it has a player
if (Auth::user() && Auth::user()->player) {
// if the player has an organization, this will be the Organzation object
// if the player does not have an organization, this will be null
$organization = Auth::user()->player->organization;
}
return view('organizations.user_index', [ 'organization' => $organization ]);
}
Yes, for this check you might get one unnecessary SQL query executed. You can get rid of this if you do:
if(Organization::find(Auth::user()->player->organization_id)
instead of
if(!is_null(Organization::find(Auth::user()->player->organization)))
This way you check organization_id that is stored in player before trying to fetch organization from the database.
I have a question about Laravel 4 and Validation concept.
I have my mysql table and my model Calass..
We can suppose that we have a field nullable...
If this field is not null how can I validate it? (for example I would an integer value < of my maxvalue)
Unless you add the 'required' rule, validation will always treat fields as optional. Thus, a rule like numeric|max:9000 will be valid if the field is null or empty string, but if it is provided it has to be a number that is smaller than 9000.
To prevent null or empty string from being saved into the database (MySQL will convert this to 0 for an integer field), you can set up a mutator for the attribute. In your model:
public function setNumberAttribute($value)
{
if ($value !== null && is_numeric($value)) {
$this->attributes['number'] = (int) $value;
}
}
http://laravel.com/docs/eloquent#accessors-and-mutators
you can create your validation into your model
Ex:
class User extends Eloquent {
protected $table = 'users';
public $timestamps = false;
public static function validate($input) {
$rules = array(
# place-holder for validation rules
);
# validation code
}
}
for more description about validation laravel book