phplaravelneoeloquent

Update dynamically validation rules by adding new data in the controller


I'm working on a web based app,and the Tech stack is : VueJS,for the presentation layer, Laravel (PHP) for RESTFUL API service,and a nosql graph based database called neo4j. Here is the problem context, I have a model called Post, and attributes that are defined here are shared all the post type,so all of them will have it:

use NeoEloquent;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Vinelab\NeoEloquent\Eloquent\SoftDeletes;

class Post extends NeoEloquent
{
protected $label = 'Post';
protected $dates = ['created_at', 'updated_at', 'deleted_at'];

/**
 * The attributes that are mass assignable.
 *
 * @var array
 */
protected $fillable = [
    'title',
    'slug',
    'body',
    'status',
    'image',
    'published_at',
    'read_time',
    'isModerate',
    'link',
    'external_id'
];

/**

protected $hidden = ['remember_token'];

/**
 * relations
 */

public function authors()
{
    return $this->belongstoMany('App\User', 'AUTHOR');
}

public function getSlugOptions(): SlugOptions
{
    return SlugOptions::create()->generateSlugsFrom('title')->saveSlugsTo('slug');
}

//Post type
public function contentType(){
  return $this->hasOne('App\ContentType','IS_OF_TYPE');
}
}

PostType : Here the type can be a File,Link,an Event,etc.Eg : [name=>'Event','description'=>'description','slug'=>'event']

class PostType extends NeoEloquent
{
    protected $label='ContentType';
    protected $dates=['created_at','updated_at','deleted_at'];

    protected $fillable=[
      "name",
      "description",
      "slug"
    ];

//Ce input contentype sera associe a plusieurs input fields
    public function customFields(){
      return $this->belongsToMany('App\CustomField',"HAS_FIELD");
    }

    public function post(){
      return $this->belongsToMany('App\Post','IS_OF_TYPE');
    }
}

And finally the CustomField model :

class CustomField extends NeoEloquent
{
  protected $label="CustomType";
  protected $dates=["created_at","updated_at","deleted_at"];

  protected $fillable=[
    "field_name",
    "field_type",
    "field_order",
    "validation_rules"
  ];


  public function contentTypes(){
    return $this->belongsToMany('App\CustomField','HAS_FIELD');
  }
}

So here is the flow: 1 - In the UI when user first open the post create post Form ,he as the common fields to fill as defined in fillable propertie of Post model.And for that,already have a Form Request for validation defines like this :

class StorePost extends FormRequest
{

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'title' => 'required|string|min:1',
            'body' => 'required',
            'status' => 'required',
            'image' => 'sometimes|image',
            'published_at' => 'required',
            'link' => 'required_if:type,==,RSS|required_if:type,==,LINK',
            'external_id' => 'sometimes'
        ];
    }
}

2 - In the same form,the user has type field in a <select> that loads all the content types from ContentType model in the database(throug ajax).Eg : Event,Link,File,... and once he chooses a type another request(ajax) goes to retrieve the the CustomField.Note that here custom field mean that,for a type Event field are send in the following format ['field_name'=>'name','field_type'=>'string','field_order'=>'1',validation_rules=>'required|max:200'],etc.. and i use those field attributes to dynamically build my PostType fields in the Front-End,and once the user fills the form and send data in the backend server:I don't have the idea of how to handle validation.What i did first and it worked was to create a Form Request for all my custom inputs field,but no i imagine if instead of just having Event,File and Link Post types i add 20.I won't create 20 validation rules.At this point my controller only knows how to validate 'Post' like this :

public function store(StorePost $request)
    {
        $data = $request->validated();
        ...
}

What i would like to do is the update the existe StorePost validation with new fields and new rules according to data comming from the front-end.So i don't have any idea of how to update the existing Form Request define in the Requests folder in my controller,and based on data that comes from front-end created new validation rules based on defines and filled fields on the front -end.I've got an idea,the consiste of fetching all input_fields validation rules based on posttypes that front-end sends to me,and then update the existing validation rules.

Note : The way that i've defined relashionship is different with Eloquent because i'm using Neoloquent that implements neo4j database.


Solution

  • You can dynamically build your validation rules using data from the request:

    use Illuminate\Http\Request;
    
    class StorePost extends FormRequest
    {
        public function authorize()
        {
            return true;
        }
    
        public function rules(Request $request)
        {
            $rules = [
                'title' => 'required|string|min:1',
                'body' => 'required',
                'status' => 'required',
                'image' => 'sometimes|image',
                'published_at' => 'required',
                'link' => 'required_if:type,==,RSS|required_if:type,==,LINK',
                'external_id' => 'sometimes'
            ];
    
            if($typeSlug = $request->get('type'))
            {
                $type = PostType::where('slug', $typeSlug)->with('customFields');
    
                // map custom fields into a `['name' => 'rules']` format
                $customFieldRules = $type->customFields->mapWithKeys(function($customField) {
                    return [$customField->field_name => $customField->validation_rules];
                });
    
                $rules = array_merge($rules, $customFieldRules);
            }
    
            return $rules;
        }
    }