0

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Christian LSANGOLA
  • 2,947
  • 4
  • 22
  • 36
  • Could you provide a sample of the payload of a request to create a `Post`? I mean, with the type and etc. – Kenny Horna May 09 '19 at 16:02
  • Okay,in the front i'll have this `axios.post('http://localhost:5000/api/posts',post)`,where `post` object has the following structure:`{title:'Mo titre',slug:'mon-titre',body:'This is the body',status:'my status',image:'base64_image',published_at:'the date',isModerate:false,link:'www.thelink.com',external_id:'the id',content_type:'content_type_id',custom_field_name:'value',custom_fileld_slug:'value',custom_field_slug:'slug'}` ...This is the structure of the payload and in the server,we have already the valdation rules of eache custom_field because only the name will be send to buil form in Vue – Christian LSANGOLA May 10 '19 at 07:22

1 Answers1

1

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;
    }
}
Thomas
  • 8,426
  • 1
  • 25
  • 49
  • Where does `$typeSlug` variable come from? – Christian LSANGOLA May 10 '19 at 07:07
  • The following comment : https://stackoverflow.com/questions/56063136/update-dynamically-validation-rules-by-adding-new-data-in-the-controller#comment98784075_56063136 bring some details – Christian LSANGOLA May 10 '19 at 07:44
  • It's defined inside the `if` statement from a 'type' request which you should replace with the name of the ` – Thomas May 10 '19 at 16:57
  • SO,intead of testing equality,i could just test if it exists?Because dont alway see why i should test equality – Christian LSANGOLA May 15 '19 at 08:57
  • Because the front-end will send the `type` to the server.So instead of `if($typeSlug = $request->get('type')){}` ,i can do `if($rquest->has('type')){}` – Christian LSANGOLA May 15 '19 at 09:05
  • Hey @Thomas ,thank you very much for your answer,i added changed it a little bit to get it work ,here is how i made the condition : `if($request->has('content_type_id')){$type = ContentType::with('customFields')->where('id',$request->get('content_type_id'))->first()...};` and the other it's all your's.Thank you very much. – Christian LSANGOLA May 25 '19 at 13:10
  • Please can take a look at this : https://stackoverflow.com/questions/56302541/add-dynamically-created-attributes-in-ressource – Christian LSANGOLA May 25 '19 at 13:14