If we create a simply route with Laravel 4 we can use where to set a regular expression for each param. passed in the URI. For example:
Route::get('users/{id}',function($id){
return $id;
})->where('id','\d+');
For every get request second param. must be digits. Problem comes when we create resources, if we use ->where() it throws an error about this is not an object.
I've tried to place where into a group, as an array as third param. in the resource but has not worked.How could we use the power of the regular expressions with the power of Laravel 4 resources?
He Joss,
I was going mad with the same question, so I did some research. All I could come to is the following conclusion: you do not need this method. In each of your controller methods you can specify your default values after each argument:
public function getPage(id = '0', name = 'John Doe'){
Next to this you can do a regular expression check, and a lot of other checks with the laravel validator like this:
//put the passed arguments in an array
$data['id'] = $id;
$data['name'] = $name;
//set your validation rules
$rules = array(
"id" => "integer"
"name" => "alpha"
);
// makes a new validator instance with the data to be checked with the given
// rules
$validator = Validator::make($data, $rules);
// use the boolean method passes() to check if the data passes the validator
if($validator->passes()){
return "stuff";
}
// set a error response that shows the user what's going on or in case of
// debugging, a response that confirms your above averages awesomeness
return "failure the give a response Sire";
}
Since most of the validation are already in the options list of laravel you might not need the regular expression check. Yet it is an option (regex:pattern).
My theory behind the none existence of the where() method in the controllers is that the method offers you the opportunity to do validation within you Route file. Since you already have this possibility in your controller there is just no need for it.
Related
I use AuthComponent with CakePHP 3.8 and now I need to do some logic in Model buildRules method but for this I need to get the current user ID.
Is there any way to pass/retrieve it without using hacks such as accessing directly from the session.
I know that it is possible to pass id via validator from controller as described in CakePHP's documentation
https://book.cakephp.org/3/en/core-libraries/validation.html#using-custom-validation-rules
And it works for validation, however, I am unable to access the validator from the inside of build rules.
When I do as described in here, I get an empty object.
https://book.cakephp.org/3/en/orm/validation.html#using-validation-as-application-rules
It seems that I am able to attach new validation rules but unable to retrieve the "Passed" provider to get the User ID.
It seems a trivial thing but a I spent quite a few hours trying to get the id in a proper way.
OK, After working a bit more, I found how to retrieve user_id inside build rules. Might be helpful to someone.
Do this in the controller
$this->ExampleModel->validator('default')->provider('passed', [
'current_user' => $this->Auth->user('id')
]);
And then put this in you buildRules method
public function buildRules(RulesChecker $rules)
{
$user_id = $this->validator('default')->provider('passed')['current_user'];
$rules->add(
function ($entity, $options) use($user_id) {
//return boolean for pass or fail
},
'ruleName',
[
'errorField' => 'some_id',
'message' => 'Some ID field is inconsistent with App logic.'
]
);
return $rules;
}
The proper way to handle this is usually using either events, or saving options.
For example to make the ID available for the application rules of all tables, you could do something like this:
$this->getEventManager()->on('Model.beforeRules', function (
\Cake\Event\Event $event,
\Cake\Datasource\EntityInterface $entity,
\ArrayObject $options
) {
$options['current_user'] = $this->Auth->user('id');
});
It would be available in the $options argument of the rule accordingly, ie as $options['current_user'].
For a specific save operation you can pass it to the options of the save() call:
$this->ExampleModel->save($entity, [
'current_user' => $this->Auth->user('id')
]);
There's also plugins that can help you with it, for example muffin/footprint.
I have a validation rule in one of my Table classes like this:
public function validationDefault(Validator $validator)
{
$validator
->scalar('comment')
->maxLength('comment', 3000)
->requirePresence('comment', 'create')
->notEmpty('comment');
return $validator;
}
This validates the comment field of an input and means it cannot be over 3000 characters in length. All of this is fine when working in PHP.
One part of my application uses a JavaScript character counter - it tells the user how many remaining characters they have in a field as they type. The js for this works fine, with the limit 3000 hardcoded.
However, I want to know if there's a way to avoid hardcoding this limit in my js? Because otherwise my code is not DRY as I'm defining the 3000 limit in multiple places and if it changes that's problematic to remember/update.
Is it possible to read the maxLength property directly from validationDefault for the comment field? This question concerns how to access the data defined in the Table class; I am fine with knowing how to pass it to js via ajax.
I haven't got any further code to show as I don't know if/how this is even possible.
CakePHP 3.5.13
To get validation rule value, you have to get Validator from Table, then get ValidationSet, and then ValidationRule, from which you can extract desired result. Sample controller code below:
$validator = $this->YourTable->getValidator("default");
$validationSet = $validator->field("comment");
$validationRule = $validationSet->rule("maxLength");
$result = $validationRule->get("pass");
Or, in just one line:
$result = $this->YourTable->getValidator("default")->field("comment")->rule("maxLength")->get("pass");
A returned value will be an array of additional arguments passed to validation rule, in your case it should look like:
array(1) {
[0]=>
int(3000)
}
Also, #ndm in his answer mentioned about other posibilities:
On form helper/context level it's also possible to read the schema, and support for transalting its length configuration and the validation rule value into a maxlength HTML attribute is being implemented for CakePHP 3.7
Further reading:
Getting validators from tables
Validator class
ValidationSet class
ValidationRule class
How to forward with GET parameters?
I have two actions in same controller. I do some magic in the first action, and if all goes well, I want it to forward to the other one, but with GET parameters created in the first one.
What I have now is this:
return $this->forward('AppBundle:default:tracked', array(), array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
Empty array in there because of a desperate effort to get it to work. Documentation tells that second array is for the query parameters, but nothing really happens, so I must be doing something wrong.
The second action tries to get the parameters like this:
/**
* #Route("/tracked", name="tracked")
*/
public function trackedAction()
{
$request = Request::createFromGlobals();
// If there is the required variables in the GET:
if($request->query->has('tracking_code') && $request->query->has('company'))
But no, variables never get there it seems.
Reason I have this kind of setup, is that user can get into the trackedAction from another place as well.
I think the proper way to get your parameters in your second action would be to do like this :
return $this->forward('AppBundle:default:tracked', array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
And your second action would be :
/**
* #Route("/tracked/{tracking_code}/{company}", name="tracked")
*/
public function trackedAction($tracking_code=null, $company=null)
{
...
}
I used the $tracking_code = null because you specified this could be accessed from another place, that maybe does not provide these parameters. This way it works for both of them.
Hope this helps.
The way that Symfony controller forwarding works is by duplicating the current the Request with the options that you pass and then re-dispatching it through the HttpKernel component. You can see this in the code. Because it's a sub-request, your second action is creating a Request from globals (i.e. $_GET etc.) which haven't changed.
The solution is to change your second action method signature to:
public function trackedAction(Request $request)
This means that the $request variable will be "local" to your action and will contain the variables you want.
In practice you should always pass the Request in to your actions this way as it makes your controllers a lot more testable and prevents strange issues like this.
Alternatively to all of the above, you could use a redirect instead which would do an HTTP redirect rather than just forwarding within the Symfony request system.
I need to check if the key is not set in the array using Laravel validator.
That would be the complete opposite of the "required" validation rule.
Basically the array will be passed to update method if it passes the validation and I want to make sure one column will not be updated.
Is there a way to check if the value "is not present"?
Thank you
EDIT:
I'm currently using Laravel 5
EDIT:
I managed to write my own validation rule by calling Validator::extendImplicit. However I get $value as null to my validation function both when I set it to null or when I don't set it at all. Is there a way to check if the value is set?
I believe I found a solution:
$validator->extendImplicit('not_present', function($attribute, $value, $parameters)
{
return !array_key_exists($attribute, $this->data);
});
I'm not calling extendImplicit statically because the Validator class object is injected to the controller of my class.
I need to access $this->data ($this referring to the Validator object) to make sure the key doesn't exist in the array being validated.
Based on the #MaGnetas answer I came up with this 2 rules that can be applied on any model.
I'm using Laravel 5.4 so putting this lines on your AppServiceProvider.php should work.
The first approach (extendImplicit and array_key_exists)
Validator::extendImplicit('not_present', function($attribute, $value, $parameters, $validator)
{
return !array_key_exists($attribute, $validator->getData());
});
Ussing $validator->getData() we could use the Validator statically.
The second approach (extend and false)
Validator::extend('not_present', function($attribute, $value, $parameters, $validator)
{
return false;
});
You could use extend because we don't need the rule to be executed if the data has not the property (because that's exactly what we want right?)
On the docs:
By default, when an attribute being validated is not present or contains an empty value as defined by the required rule, normal validation rules, including custom extensions, are not run. more info
Important: The only difference is that using extend, empty strings will not run the validation. But if you have setting TrimStrings and ConvertEmptyStringsToNull on your middleware (which AFAIK is the default option) there will be no problem
No there is no build in validtion rule for this, but you can create your own validation rule.
The simplest way to do this:
Validator::extend('foo', function($attribute, $value, $parameters)
{
// Do some stuff
});
And check if key exists.
More information:
http://laravel.com/docs/4.2/validation#custom-validation-rules
For people looking for the not_present logic in 7.x apps (applicable for all versions), remember that you can simply use the validated data array for the same results.
$validatedKeys = $request->validate([
'sort' => 'integer',
'status' => 'in:active,inactive,archived',
]);
// Only update with keys that has been validated.
$model->update(collect($request->all())->only($validatedKeys)->all());
my model has more attributes but only these two should be updatable, therefore I too were looking for an not_present rule but ending up doing this as the results and conceptual logic is the very same. Just from another perspective.
I know this question is really old but you can also use
'email' => 'sometimes|required|not_regex:/^/i',
If the email is present in the request, the regex will match any characters in the request and if the email is an empty string but is present in request the sometimes|required will catch that.
Is there any way I can have a route set up with one regex section that doesn't get passed as a parameter?
For example:
Route::get('{string}/method/{id}', function($id)
{
return 'only one parameter passed, ID is ' . $id;
});
Specifically I'm routing to a controller and the methods need to be compatible with routes coming from elsewhere, which don't include this first parameter.
The most important thing is that, routes has to be matched according to it's declaration, for example, if you define a route like your example here
Route::get('{string}/method/{id}', function($id)
{
return 'only one parameter passed, ID is ' . $id;
});
Then the requested url has to be matched with same numbers of parameters including the http method (GET here) and in this case, the route will match only with something like this
httP//example.com/something/method/10
Here the second parameter 10 is not bound to be digits because you didn't make it to be a digit using where(...) so, it could be anything but two parameters must be required.
As an alternative, you may define a missing method in your controller like this (An idea only)
public function missingMethod($args = array())
{
// $args will contain all parameters
}
This is a special method in Laravel that any controller may contain it and whenever a non-existing method will be called in that controller, then this missingMethod would be called and all the parameters will be passed to it's $args parameter as an array, so if you have two parameters in the url and calling method is missing in the controller then you may get those parameters within this method scope, something like [param1, param2] and from this method you may call your desired method depending on the count of params.
So, if you just point the route's action to this controller which has the missing method then from the missingMethod you may call another method using different parameters according to your other method.
Also check PHP Overloading and call_user_func_array to get the real idea of missingMethod.