valid constraint does not support group options in symfony2 - validation

I need to cascade validation in symfony2 form unless for specified group.
here symfony team told that group option is not supported in Valid constraint
https://github.com/symfony/symfony/issues/4893
How to do it ?
Details:
I have a User Entity has address property which is a foreign key to Address Entity. Also i have Entity called business having User as property and also Address property. I need to validate address for User but, without validating it When User is a property of Business...
Schema
Class Address {
...
}
Class User {
/**
* #Assert\Valid(groups={"user"})
*/
private $address;
}
Class Business {
/**
* #Assert\Valid(groups={"business"})
*/
private $user;
/**
* #Assert\Valid(groups={"business"})
*/
private $address;
}
So I need to validate The address inside User only for User Forms but not for Business.
Thank you

I was faced with the same problem (Symfony 3).
I have a entity UserInfo with two fields linked to one entity Place.
And I need to validate both fields in one case, and one field in another case.
And didn't want to move constraints into Form.
In first atemt I used a Callback constraint to check group and validate one or both fields. It was fine. But input fields in form wasn't marked as invalid. All errors was displayed at top of the form.
Then I simply created own validator. Thanks to this I can specify needed groups for each field. And all invalid input fields in form marked accordingly.
/**
* #Annotation
* #Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
class ValidGroupAware extends Constraint
{
}
class ValidGroupAwareValidator extends ConstraintValidator
{
/**
* Checks if the passed value is valid.
*
* #param mixed $value The value that should be validated
* #param Constraint $constraint The constraint for the validation
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof ValidGroupAware) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\ValidGroupAware');
}
$violations = $this->context->getValidator()->validate($value, [new Valid()], [$this->context->getGroup()]);
/** #var ConstraintViolation[] $violations */
foreach ($violations as $violation) {
$this->context->buildViolation($violation->getMessage())
->setParameters($violation->getParameters())
->setCode($violation->getCode())
->setCause($violation->getCause())
->setPlural($violation->getPlural())
->setInvalidValue($violation->getInvalidValue())
->atPath($violation->getPropertyPath())
->addViolation();
}
}
}

Ok, I have a solution. Callback constraints do have 'groups' option and we can use it here. In the callback we will call validation for the required entity.
I will use php code for adding constraints, but you could use annotations.
User entity
// ...
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(new Assert\Callback(array(
'methods' => array('validAddress'),
'groups' => array('user'),
)));
}
public function validAddress(ExecutionContextInterface $context)
{
$context->validate($this->address, 'address', $context->getGroup());
}
Business entity
// ...
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(new Assert\Callback(array(
'methods' => array('validUser'),
'groups' => array('business'),
)));
$metadata->addConstraint(new Assert\Callback(array(
'methods' => array('validAddress'),
'groups' => array('business'),
)));
}
public function validUser(ExecutionContextInterface $context)
{
$context->validate($this->user, 'user', $context->getGroup());
}
public function validAddress(ExecutionContextInterface $context)
{
$context->validate($this->address, 'address', $context->getGroup());
}
PS Of course my code can be optimized

You can add your validation rules in your User FormType.
Don't forget to delete your annotation.
Follow this link to learn more about validation in form type

Related

Laravel/Livewire: Use withTrashed() on model route binding on to show deleted records

In the list I display the latest topic, including those that is deleted.
function latest()
{
return Topic::withTrashed()->latest();
}
For displaying a single topic I have a Livewire component with that topic passed into it.
class ShowTopic extends Component
{
public $topic;
public function mount(Topic $topic)
{
$this->topic = $topic;
}
public function render()
{
return view('livewire.show-topic', [
'topic' => $this->topic,
]);
}
}
But when I go to a single topic that is deleted, it doesn't show. How can I use withTrashed() on model route bindings to show deleted records with my Livewire component?
You can overwrite the resolveRouteBinding() method on your Eloquent model, and conditionally remove the SoftDeletingScope global scope.
Here I'm using a policy for that model to check if I can delete the model - and if the user can delete it, they can also see it. You could implement any logic you want, or remove the global scope for all requests if that is more suitable for your application.
use Illuminate\Database\Eloquent\SoftDeletingScope;
class Topic extends Model {
// ...
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #param string|null $field
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
// If no field was given, use the primary key
if ($field === null) {
$field = $this->getKey();
}
// Apply where clause
$query = $this->where($field, $value);
// Conditionally remove the softdelete scope to allow seeing soft-deleted records
if (Auth::check() && Auth::user()->can('delete', $this)) {
$query->withoutGlobalScope(SoftDeletingScope::class);
}
// Find the first record, or abort
return $query->firstOrFail();
}
}

Symfony2: How to validate an entity by taking into account the currently logged in user?

I'm doing a custom validation using callback inside the User entity.
Therefore I need to get the User object of the currently logged user.
In controller I can do it like this:
$user= $this->get('security.context')->getToken()->getUser();
But how should I do it inside the User entity?
If you want to validate an object with any logic related to an external dependency (in your case the #security.context service to get the current user) ... you should:
create a custom validation-constraint
create a custom validator service to be used by the constraint
inject the #security.context service into the validator
use this newly created validation constraint to validate your entity
The solution is described in the documentation chapter Validators with Dependencies
This video shows why it's absolutely necessary to have the following RedBull validator.
Model/Entity
use FamilyGuy\Validator\Constraints\Peter as PeterIsNotAllowedToOrder;
class Order
{
/** #PeterIsNotAllowedToOrder/RedBull */
public $drink;
Config
# app/config/services.yml
services:
validator.red_bull:
class: FamilyGuy\Validator\Constraints\Peter\RedBullValidator
# for symfony < 2.6 use #security.context
arguments: ["#security.token_storage"]
tags:
- name: "validator.constraint_validator"
alias: "peter_red_bull_constraint_validator"
Constraint
use Symfony\Component\Validator\Constraint;
namespace FamilyGuy\Validator\Constraints\Peter;
/**
* #Annotation
*/
class RedBull extends Constraint
{
/** #var string */
public $message = 'Peter... You are not allowed to order %drink%.';
/** #return string */
public function validatedBy()
{
// has to match the validator service's alias !
return 'peter_red_bull_constraint_validator';
}
}
Validator:
// For symfony < 2.6 use SecurityContextInterface
// use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
namespace FamilyGuy\Validator\Constraints\Peter;
class RedBullValidator extends ConstraintValidator
{
/** #var TokenStorageInterface|SecurityContextInterface */
protected $tokenStorage;
/** #param TokenStorageInterface|SecurityContextInterface $token_storage */
public function __construct(TokenStorageInterface $token_storage)
{
$this->tokenStorage = $token_storage;
}
public function validate($drink, Constraint $constraint)
{
$currentUser = $this->tokenStorage->getToken()->getUser()->getName();
if ($currentUser !== "Peter") {
return;
}
if ( $drink !== "RedBull" ) {
return
}
$this->context->buildViolation($constraint->message)
->setParameter('%drink%', $drink)
->addViolation()
;
}
You can't and you shouldn't use the Callback constraint to validate against any external dependency.
Don't try to inject any validation dependency into your domain model or entities directly.
Validation logic should be kept out of entities in general.
Annotations are already some sort of soft-coupling between entities and the symfony validator. That's why it's recommended to use xml configuration for doctrine- and validation-mappings usually.

Laravel Model conditional formatting

I have a database and model called Vote_actions that looks like this:
id
group_id
user_id
action_type
anonymous (boolean)
User can ask to be anonymous (that would make the boolean value to be true).If that is the case, I want to change the group_id and user_id from the returned model to -1.
Is there a way in laravel that I can do it ?
I know this question is old. I was looking for a way to hide some fields on certain conditions, external conditions like Auth Roles, and internal conditions like Model attributes, and I found a very flexible way to hide them.
And since I saw the other OP's duplicated post Laravel Hidden Fields On Condition asking for hiding field instead, So I'm gonna share it with you.
I know a mutator can change the value of its field, but to Hide it, you need :
the $hidden array attribute
the constructor __Construct() (optional)
to override method newFromBuilder method of Laravel Model
Here are the processes in the Model app\Vote_actions.php:
Hidden. Let's say you normally want to hide the fields created_at and updated_at of Laravel, you use:
protected $hidden = ['created_at', 'updated_at'];
External Conditions. Now let's say if the Authenticated User is Staff you want to unhide them:
public function __Construct()
{
parent::__construct();
if(\Auth::check() && \Auth::user()->isStaff()) {
// remove all fields so Staff can access everything for example
$this->hidden = [];
} else {
// let's hide action_type for Guest for example
$this->hidden = array_merge($this->hidden, ['action_type'];
}
}
Internal Conditions Let's say now you want to hide anonymous field is its value is true:
/**
* Create a new model instance that is existing.
*
* #param array $attributes
* #param array $connection
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function newFromBuilder($attributes = array(), $connection = null)
{
$instance = parent::newFromBuilder($attributes, $connection);
if((bool)$instance->anonymous === true) {
// hide it if array was already empty
// $instance->hidden = ['anonymous'];
// OR BETTER hide single field with makeHidden method
$instance->makeHidden('anonymous');
// the opposite is makeVisible method
}
return $instance;
}
You can't play with hidden attributes and method inside mutators, that's their weakness when we need to hide instead of changing values.
But in any case, understand that calling modification on high load of hundredths of rows can be costly in time.
You are leaning towards an edge case, with special conditions.
Make use of accessors:
class VoteActions extends \Eloquent {
public $casts = [
'anonymous' => 'boolean'
];
...
/**
* Accessors: Group ID
* #return int
*/
public function getGroupIdAttribute()
{
if((bool)$this->anonymous === true) {
return -1;
} else {
return $this->group_id;
}
}
/**
* Accessors: User ID
* #return int
*/
public function getUserIdAttribute()
{
if((bool)$this->anonymous === true) {
return -1;
} else {
return $this->user_id;
}
}
}
Official Documentation: https://laravel.com/docs/5.1/eloquent-mutators#accessors-and-mutators
However, i would recommend that you set the value in the database directly to -1 where necessary so as to preserve the integrity of your application.
Of course you can easily do that. Read about accessors (getters):
https://laravel.com/docs/5.1/eloquent-mutators
Example:
function getUserIdAttribute()
{
return $this->anonymous ? -1 : $this->user_id;
}
function getGroupIdAttribute()
{
return $this->anonymous ? -1 : $this->group_id;
}

Dynamically hide certain columns when returning an Eloquent object as JSON?

How do dynamically hide certain columns when returning an Eloquent object as JSON? E.g. to hide the 'password' column:
$users = User::all();
return Response::json($users);
I'm aware I can set protected properties in the model ($hidden or $visible), but how do I set these dynamically? I might want to hide or show different columns in different contexts.
$model->getHidden();
$model->setHidden(array $columns);
$model->setVisible(array $columns);
From Lavarel 5.3 Documentation :
Temporarily Modifying Attribute Visibility
If you would like to make some typically hidden attributes visible on a given model instance, you may use the makeVisible method. The makeVisible method returns the model instance for convenient method chaining:
return $user->makeVisible('attribute')->toArray();
Likewise, if you would like to make some typically visible attributes hidden on a given model instance, you may use the makeHidden method.
return $user->makeHidden('attribute')->toArray();
I've found a complete solution around the problem with using $model->setHidden(array $columns);
Lets say, for example, that you would like to decide in the controller exactly which fields to return. Updating only the model's hidden forces you to go over each model before you return an array of models for example. The problem becomes even worse when those models have relationships that you would also like to change. You have to loop over each model, set the hidden attribute, and then for each also set the relationships hidden. What a mess.
My solution involves creating a static member for each model that when present, updates the visible/hidden attribute just before the call to "toArray":
<?php
trait DynamicHiddenVisible {
public static $_hidden = null;
public static $_visible = null;
public static function setStaticHidden(array $value) {
self::$_hidden = $value;
return self::$_hidden;
}
public static function getStaticHidden() {
return self::$_hidden;
}
public static function setStaticVisible(array $value) {
self::$_visible = $value;
return self::$_visible;
}
public static function getStaticVisible() {
return self::$_visible;
}
public static function getDefaultHidden() {
return with(new static)->getHidden();
}
public static function geDefaultVisible() {
return with(new static)->getVisible();
}
public function toArray() {
if (self::getStaticVisible())
$this->visible = self::getStaticVisible();
else if (self::getStaticHidden())
$this->hidden = self::getStaticHidden();
return parent::toArray();
}
}
As an added bonus, I expose a way to the model's default hidden/visible that you may have set in your model's class.
Don't to forget to add the trait
class Client extends Eloquent {
use DynamicHiddenVisible;
}
Finally, in the controller, before returning your model, decide on visible/hidden attributes:
public function getIndex($clientId) {
// in this specific call, I would like to hide the "special_type" field of my Client model
$hiddenFields = Client::getDefaultHidden();
array_push($hiddenFields, "special_type");
Client::setStaticHidden($hiddenFields);
return Client::find($clientId)->toJson();
}
I don't believe it is the job of the ORM to worry about presentation logic, and that is what JSON is. You'll aways need to cast data to various types as well as hide things and sometimes create a buffer zone to rename things safely.
You can do all of that with Fractal which I built for exactly this reason.
<?php namespace App\Transformer;
use Acme\Model\Book;
use League\Fractal\TransformerAbstract;
class BookTransformer extends TransformerAbstract
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'author'
];
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => (int) $book->yr,
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
],
];
}
/**
* Include Author
*
* #return League\Fractal\ItemResource
*/
public function includeAuthor(Book $book)
{
$author = $book->author;
return $this->item($author, new AuthorTransformer);
}
}
Embedding (including) stuff might be a bit more than you need right now, but it can be very handy too.
In 5.4 you can hide and show attributes dinamically:
$model->makeVisible('attribute');
$model->makeHidden('attribute');
Laravel docs
In addition to #deczo's answer - I feel the $hidden variable is not really designed to be used dynamically. It is more to protect specific data from ever been incorrectly displayed (such as 'password').
If you want specific columns - you should probably just be using a select statement and just get the specific columns you want.
For Laravel 5.3 or greater version,
If you want to make multiple attributes temporary hidden or visible using single statement, you may use model->makeVisible() and model->makeHidden() methods with passing array of attributes.
For example, to hide multiple attributes,
$user->makeHidden(["attribute1", "attribute2", "attribute3"]);
And to make visible multiple attributes,
$user->makeVisible(["otherAttribute1", "otherAttribute2", "otherAttribute3"]);
In the Model:
protected $hidden = [
'your_field_1',
'your_field_2',
];
You can override the getHidden method in order to hide certain columns dynamically:
class FooModel extends Model
{
public function getHidden()
{
// do here your validations and return
// the columns names with the specific criteria
// you need
return ['columnName1', 'columnName2'];
}
}
Made a package for this that uses Model Policies.
https://github.com/salomoni/authorized-attributes
Use the Salomoni\AuthorizedAttributes trait
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Salomoni\AuthorizedAttributes;
class Post extends Model
{
use AuthorizedAttributes;
/**
* The attributes that should be hidden for serialization.
*
* #var array
*/
protected $hidden = ['author_comments'];
}
Create and register a model policy. Add methods for the hidden attributes in camel-case prefixed with see.
namespace App\Policies;
use App\User;
class PostPolicy
{
/**
* Determine if a post author_comments-atrribute can be seen by the user.
*
* #param \App\User $user
* #return bool
*/
public function seeAuthorComments(User $user)
{
return $user->isAuthor();
}
}

Is it possible to alter error message in Laravel's Validator callback?

I have a custom validator set-up like this:
Validator::extend('valid_username', 'ProfileController#valid_username');
Then I have the following method which handles the validation. It checks both if the username already exists, and if the username contains valid characters.
public function valid_username($attribute, $value, $parameters)
{
$u = User::where('username', $value)->get();
if ($u->count())
{
// here I would like to return "Username already taken."
return FALSE;
}
else if (preg_match("/^[A-Za-z0-9#\.\-_]+$/", $value))
{
return TRUE;
}
else
{
// here I would like to return "Username contains invalid characters."
return FALSE;
}
}
I would like to alter the error message returned by this validator depending on which error caused the validation to fail. However, I don't know how to do this. In my language files I have set up the following line for the validator:
"valid_username" => "This username is already taken or contains invalid characters."
Is it possible with Laravel to return a specific error message? Or do I have to split this validation up in two custom validation rules? This might not be a problem in this case, but especially if database access is involved I would prefer to validate a retrieved Eloquent model in one validator instead of instantiating an Eloquent object twice.
After consulting the code, the answer is "not out of the box". You can, however, extend everything and make that work.
The process, which I don't have time to completely do at the moment (sorry!), would be to create a class extending Validator, making that functionality work, and then using a new ServiceProvider to replace Laravel's $app['validator'] with your own.
That process, a little more concretely, goes like this something like this:
<?php namespace MyLib\Validation;
class Validator extends \Illuminate\Validation\Validator {
// Fancy validation logic to be able to set custom messages
}
Then, you need to extend the Factory to return your new Validator:
<?php namespace MyLib\Validation;
class Factory extends \Illuminate\Validation\Factory {
// Change this method
/**
* Resolve a new Validator instance.
*
* #param array $data
* #param array $rules
* #param array $messages
* #return \MyLib\Validation\Validator
*/
protected function resolve($data, $rules, $messages)
{
if (is_null($this->resolver))
{
// THIS WILL NOW RETURN YOUR NEW SERVICE PROVIDER SINCE YOU'RE
// IN THE MyLib\Validation NAMESPACE
return new Validator($this->translator, $data, $rules, $messages);
}
else
{
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages);
}
}
}
...and finally, extend the Validation service provider, use your new Factory, and then replace the default ValidationServiceProvider with your own.
<?php namespace MyLib\Validation;
class ValidationServiceProvider extends \Illuminate\Validation\ServiceProvider {
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerPresenceVerifier();
$this->app['validator'] = $this->app->share(function($app)
{
// THIS WILL NOW RETURN YOUR FACTORY SINCE YOU'RE
// IN THE MyLib\Validation NAMESPACE
$validator = new Factory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence
// of values in a given data collection, typically a relational database or
// other persistent data stores. And it is used to check for uniqueness.
if (isset($app['validation.presence']))
{
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
So anyway, that's one way to extend the Validation library with your own code. I didn't solve the issue of adding your own messages, but this will show you how, if you can read the core code and see how to add that functionality in, to go about making it work in your app.
Last note:
You may want to see how Laravel handles using Database "stuff" within validation rules - While this may not affect your application (unless it gets big!) you may want to consider using a Repository pattern of some sort and using that in your Validator::extend() call instead of the User class directly. Not necessary, just a note for something to check out.
Good luck and don't be afraid to RTFC!
Instead of making your own validation rule that does validates two things (which you shouldn't really do, validate one thing at a time), you can use the unique rule and then make your own rule that validates the characters of the username.
For example:
$rules = ['username' => 'required|username|unique:users,username'];
Where the username rule is your custom rule that ensures it contains the correct characters.
Maybe a bit "dirty" but it works:
The controller validates the input with something
$rules = array(
'title' => 'no_collision:'.$input['project_id']
);
In the validator function flash the message to the Session before returning false:
//...
public function validateNoCollision($attribute, $value, $parameters)
{
$project = Project::find($parameters[0]);
if($value == $project->title){
Session::flash('colliding_message','This collides with '.$project->title($).' created by '.$project->user->name;
return false;
}else{
return true;
}
}
In the view do something like the following:
#if($errors->has('title'))
<span class="help-block">{{ Session::get('colliding_message') }}</span>
#endif

Resources