Checking backed enum's scalar value in Laravel unique validation where clause - enums

In Laravel 9 with PHP 8.1, I can not compare backed enum in laravel validation rules where you can use ->where clause. It needs a scalar value to compare. Are there any other ways to compare with an enum object rather than scalar value?
validator($attributes, [
'manager_id' =>
[
'required',
'integer',
Rule::exists(User::getTableName(), 'id'),
Rule::unique(JProject::getTableName(), 'manager_id')
->where('status', JProjectStatus::Active)
]
])->validate();
JProjectStatus is a backed enum
enum JProjectStatus: string
{
case Active = 'active';
case Inactive = 'inactive';
case Blocked = 'blocked';
}
When I'm trying to check the manager is unique where the project is active or not, I encountered a type error and I totally understand that it is comparing string with enum object.
message "str_replace(): Argument #3 ($subject) must be of type
array|string, App\Enums\JProjectStatus given" exception "TypeError"
But if I write like below which is a scaler value that's totally fine with str_replate().
validator($attributes, [
'manager_id' =>
[
'required',
'integer',
Rule::exists(User::getTableName(), 'id'),
Rule::unique(JProject::getTableName(), 'manager_id')->where('status', JProjectStatus::Active->value)
]
])->validate();
Because now 'JProjectStatus::Active->value' is a scalar value of 'active'
Sure, I can pass a closure in where condition but want to know the best practice. My question is, Is it okay to write like this, or is there any other way to write best practice with enum object.

Yes, it is possible, i'm using my backed enum in this way
$request->validate([
'state' => ['required', new Enum(ActiveStatus::class)],
]);
and import this file in controller
use Illuminate\Validation\Rules\Enum;
Laravel Enums only works with Laravel 9 with PHP-8.1

Related

Laravel validation for accepting only predefined values from api

Basically I wrote an api in laravel, The api should return a validation error if any of the key has wrong values (spelling mistakes,extra space). To make more clarity, in the web interface these key values are from select boxes . so users do not get to type anything.
First consider using in_array function for every inputs. I think that works. But i would like to know if there is anything for laravel specific.
something like
'email' => 'required | email| 'sandy#stackoverflow.com'
to make it ease. I could not find it unfortunately. It seems not that hard.
I believe you can achieve this with in, for example:
$rule = [
'email' => 'in:sandy#stackoverflow.com',
];
Or you could try it including the Rule namespace as described in the docs here
use Illuminate\Validation\Rule;
Validator::make($data, [
'zones' => [
'required',
Rule::in(['first-zone', 'second-zone']),
],
]);

Laravel 5.6. How to test JSON/JSONb columns

$this->assertDatabaseHas() not working with JSON/JSONb columns.
So how can I tests these types of columns in Laravel?
Currently, I have a store action. How can I perform an assertion, that a specific column with pre-defined values was saved.
Something like
['options->language', 'en']
is NOT an option, cause I have an extensive JSON with meta stuff.
How can I check the JSON in DB at once?
UPD
Now can be done like that.
I have solved it with this one-liner (adjust it to your models/fields)
$this->assertEquals($store->settings, Store::find($store->id)->settings);
Laravel 7+
Not sure how far back this solution works.
I found out the solution. Ignore some of the data label, Everything is accessible, i was just play around with my tests to figure it out.
/**
* #test
*/
public function canUpdate()
{
$authUser = UserFactory::createDefault();
$this->actingAs($authUser);
$generator = GeneratorFactory::createDefault();
$request = [
'json_field_one' => [
'array-data',
['more-data' => 'cool'],
'data' => 'some-data',
'collection' => [
['key' => 'value'],
'data' => 'some-more-data'
],
],
'json_field_two' => [],
];
$response = $this->putJson("/api/generators/{$generator->id}", $request);
$response->assertOk();
$this->assertDatabaseHas('generators', [
'id' => $generator->id,
'generator_set_id' => $generator->generatorSet->id,
// Testing for json requires arrows for accessing the data
// For Collection data, you should use numbers to access the indexes
// Note: Mysql dose not guarantee array order if i recall. Dont quote me on that but i'm pretty sure i read that somewhere. But for testing this works
'json_field_one->0' => 'array-data',
'json_field_one->1->more-data' => 'cool',
// to access properties just arrow over to the property name
'json_field_one->data' => 'some-data',
'json_field_one->collection->data' => 'some-more-data',
// Nested Collection
'json_field_one->collection->0->key' => 'value',
// Janky way to test for empty array
// Not really testing for empty
// only that the 0 index is not set
'json_field_two->0' => null,
]);
}
Note: The below solution is tested on Laravel Version: 9.x and Postgres version: 12.x
and the solution might not work on lower version of laravel
There would be two condition to assert json column into database.
1. Object
Consider Object is in json column in database as shown below:
"properties" => "{"attributes":{"id":1}}"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->attributes->id"=>1
]);
2. Array
Consider array is in json column as shown below:
"properties" => "[{"id":1},{"id":2}]"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->0->id"=>1,
"properties->1->id"=>2,
]);
Using json_encode on the value worked for me:
$this->assertDatabaseHas('users', [
'name' => 'Gaurav',
'attributes' => json_encode([
'gender' => 'Male',
'nationality' => 'Indian',
]),
]);

Validate that each comma separated value exists in database?

Using Laravel 5.4, my users have an auto-complete helper to put values into an input. I want to validate that each of the values exists in the database when inserting.
Inputted value for "unit" : "12,13,14"
How do I check that, unit "12" and unit "13" and unit "14" exist in the database before doing the insert?
$units = array_filter(array_unique(explode(",", $request->unit)));
// input "12,13,14" becomes [12,13,14]
$this->validate($request,[
'unit' => 'required|exists:units.id,'.$units,
]);
Do I have to use a custom validation rule, or does laravel have something handy like 'required|existsAllValuesInThisArray' sorta thing? Haven't found anything in documentation about it.
I also found this, but it's for like multiple select fields or checkboxes sorta thing from the looks of it.
$validator = Validator::make($request->all(), [
'person.*.email' => 'email|unique:users',
'person.*.first_name' => 'required_with:person.*.last_name',
]);
Update : I ended up using javascript to split the input into an array before sending it off for processing. So my input name became "units[]" instead of "units"
Try the following:
$this->validate($request,[ 'unit.*' => 'required|exists:units.id,'.$units, ]);
Since $units is an array, the rule unit.* should check for each element of the array.

How to allow empty value for Laravel numeric validation

How to set not require numeric validation for Laravel5.2? I just used this Code but when i don't send value or select box haven't selected item I have error the val field most be numeric... I need if request hasn't bed input leave bed alone. leave bed validate ...
$this->validate($request, [
'provinces_id' => 'required|numeric',
'type' => 'required',
'bed' => 'numeric',
]);
If I understood you correctly, you're looking for sometimes rule:
'bed' => 'sometimes|numeric',
In some situations, you may wish to run validation checks against a field only if that field is present in the input array. To quickly accomplish this, add the sometimes rule to your rule list
In Laravel 6 or 5.8, you should use nullable. But sometimes keyword doesn't work on that versions.
Use sometimes instead of required in validation rules. It checks if only there is a value. Otherwise it treats parameter as optional.
You may need nullable – sometimes and
present didn't work for me when combined with integer|min:0 on a standard text input type - the integer error was always triggered.
A Note on Optional Fields
By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel class. Because of this, you will often need to mark your "optional" request fields as nullable if you do not want the validator to consider null values as invalid.
Tested with Laravel 6.0-dev
Full list of available rules
In laravel 5.5 or versions after it, we begin to use nullable instead of sometimes.
according to laravel documentation 8 you must to set nullable rule
for example:
$validated = $request->validate([
'firstName' => ['required','max:255'],
'lastName' => ['required','max:255'],
'branches' => ['required'],
'services' => ['required' , 'json'],
'contract' => ['required' , 'max:255'],
'FixSalary' => ['nullable','numeric' , 'max:90000000'],
'Percent' => ['nullable','numeric' , 'max:100'],
]);
in your case :
$this->validate($request, [
'provinces_id' => 'required|numeric',
'type' => 'required',
'bed' => 'nullable|numeric',
]);

Yii2 : Active Record Column Aliases

I'm using Yii2 framework with advanced template.
I get problem with column alias in my controller file, here's my code:
$models = new ActiveDataProvider([
'query' => User::find()->select(['member'=>'fullname'])
]);
The above query equivalent with:
SELECT fullname AS member FROM User;
I send the data to the view using this code:
return $this->render('view', [
'model' => $models,
]);
I want to call the data in my view using GridView widget, here's my code:
echo GridView::widget([
'dataProvider' => $model,
'columns' => [
'member',
],
]);
However, I got an error that tell me the 'member' parameter is not defined.
How can I show the data of my query by calling the column name? (in my case it using alias)
I really appreciate any kind of helps!!
You should simply declare this attribute in your model :
class User extends ActiveRecord
{
public $member;
Read more : https://www.yiiframework.com/doc/guide/2.0/en/db-active-record#selecting-extra-fields
ActiveDataProvider works only with model attributes. member obviously is not presented there.
First of all, maybe it's better to refactor column names to be more clear instead of writing aliases? I don't see any benefit in your example.
If you nevertheless need to use aliases, as alternative for adding additional properties to class, you can work with them with help of ArrayDataProvider and SqlDataProvider.
Examples of usage:
ArrayDataProvider:
use yii\data\ArrayDataProvider;
$dataProvider = new ArrayDataProvider([
'allModels' => User::find()->select(['member' => 'fullname'])->all(),
]);
SqlDataProvider:
use yii\data\SqlDataProvider;
use yii\db\Query;
...
$query = (new Query())
->select(['member' => 'fullname'])
->from('users');
$command = $query->createCommand();
$dataProvider = new SqlDataProvider([
'sql' => $command->sql,
'params' => $command->params,
'totalCount' => $query->count(),
]);
For more details and features of usage please see official docs.
For your case it's better to use ArrayDataProvider, SqlDataProvider is for more complex queries.
In case of one alias and using model methods adding additional attribute as suggested by soju can be better.
But in my opinion it's useless and it's better to refactor column names in case of some ambiguity.

Resources