1

I am very green at Laravel (first project) so bear with me if I'm making novice mistakes. I'm trying to create this project while running through Laracasts, so I'm using his suggestions. I'm using Laravel 5.4 and PHP 7.1.4

I have a checkbox on a form for a Boolean field. If the checkbox is not checked when the form is submitted then it returns null. I do not want null values for Boolean's so I have a validator ensuring it only accepts true/false values. In order for this to work I had to create a mutator to change the value to false if it was null.

I have two models, Item and ItemNote. I'm trying to create the ItemNote from the Item model. On the Item page there is a place to add the ItemNote which runs through to the ItemNoteController, I then call a method in Item to add the ItemNote. The issue is I can't get the mutator to run in the ItemNote model so the validation fails because the Boolean field (calendar_item) is null.

I was at first trying to create ItemNote from the relationship with Item, according to this stack overflow Laravel 5 mutators only work when I create a record and not when I update a record answer the mutator will not run when creating via a relationship $this->notes()->create($request->all()). You have to use the model $this->notes->create($request->all()) notice the absence of parenthesis after notes. So I've tried everything I can possibly think of to try to create the object via the model and I still can't get the mutator to run.

Here are the relationship declarations in my models:

Item

public function notes() { return $this->hasMany(ItemNote::class); }

ItemNote

public function item() { return $this->belongsTo(Item::class); }

Mutator in ItemNote for calendar_item

protected function setCalendarItemAttribute($value) { $this->attributes['calendar_item'] = isset($value) ? $value : FALSE; }

The validation rules in ItemNote

public static $validationRules = array('note_date' => 'required|date',
                                           'resolve_date' => 'nullable|date',
                                           'notes' => 'required|string',
                                           'cost' => 'nullable|numeric',
                                           'calendar_item' => 'required|boolean',
                                           'attachment_path' => 'nullable|string|max:200');

This is the action in ItemNoteController that runs when adding a ItemNote from the Item page

public function store(Item $item)
{
    $this->validate(request(), ItemNote::$validationRules);

    $item->addNote(new ItemNote(request(['item_note_category_id', 'note_date', 'resolve_date', 
                                        'notes', 'cost', 'calendar_item', 'attachment_path'])));

    return back();
}

Here is the function addNote in the Item model

public function addNote(ItemNote $note)
{
    $this->item_note->save($note);
}

Here are the different things I have tried in addNote, they all fail to run the mutator. The create statements have the field assignments listed out but I removed them here for brevity.

$this->notes->save($note);
$this->notes()->save($note);
$this->item_note->save($note);
$this->notes->create
$this->item_notes->create
$this->item_notes()->create
$this->item_note->create
$this->item_note()->create
$this->ItemNote->create
$this->ItemNote()->create
ItemNote::create

All of the above work, although I would think $this->item_notes->create shouldn't work at all because the relationship name is notes but it doesn't complain which makes me think it may not be getting to this code and it's failing on the validate statement in the controller. How do I get the mutators run before the validation? Or is there a better way to clean up the data before the validation?

I also tried putting the item_id field in the validation rules but that always fails because the item_id is not assigned until I create the object via the relationship. I'd like to require it but haven't figured out how to get it assigned in the request.

Any help is appreciated. Sorry for the long post.

PhishTail
  • 15
  • 6
  • Don't know what't wrong, but this syntax is correct: $this->notes()->save($note); – C Taque Aug 14 '17 at 20:15
  • what is the result of dd($item->addNote(new ItemNote(request(...)))); – C Taque Aug 14 '17 at 20:24
  • I put dd($item->addNote(new ItemNote(request(['item_note_category_id', 'note_date', 'resolve_date', 'notes', 'cost', 'calendar_item', 'attachment_path'])))); before the validate statement and I got FatalThrowableError call to member function save() on null. Maybe I should put it somewhere else, after the validate? – PhishTail Aug 14 '17 at 20:29
  • Did you fix the addNote method ? (see my first comment) – C Taque Aug 14 '17 at 20:33
  • If i put that dd line after the validate then it runs and shows the error that calendar_item field is required – PhishTail Aug 14 '17 at 20:34
  • Yeah I already tried that, see the list of things I've tried at the end of the post. I tried it again just now and still get the same issue, the mutator isn't running so I get that the calendar item field is required – PhishTail Aug 14 '17 at 20:35
  • So the issue is not on the save method. The request does'nt passes the validation and the view is returned, that's why the entity isn't saved – C Taque Aug 14 '17 at 20:37
  • ok so how do I get the mutator to run before the validate? – PhishTail Aug 14 '17 at 20:38
  • I don't know, i've never used that – C Taque Aug 14 '17 at 20:42

1 Answers1

2

Your mutators are on your model. Whereas you're using the ValidatesRequests controller trait to validate your request input data. So your mutators are only being invoked after you've run your validation.

I therefore see that you have two options.

a. Modify your HTML to ensure you always receive a boolean value. For example, use a hidden input with a default value. This value will only be submitted, if the checkbox isn't checked.

<input name="example" type="hidden" value="0">
<input name="example" type="checkbox" value="1">

b. Hydrate your model, to invoke your mutators, then run your validation.

$itemNote = new ItemNote($request->all());
$request->merge($itemNote->toArray());

$this->validate($request, ItemNote::$validationRules);
// ...
fubar
  • 16,918
  • 4
  • 37
  • 43
  • Here is what I tried
    $note = new ItemNote(request(['item_note_category_id', 'note_date', 'resolve_date', 'notes', 'cost', 'calendar_item', 'attachment_path']));
    $request->merge($note->toArray());
    $this->validate($request, ItemNote::$validationRules);
    $item->addNote($note);
    I get an error undefined variable request on the line for merge. I tried adding $request = new Request(); above the code but got more errors.
    – PhishTail Aug 14 '17 at 23:03
  • sorry I can't figure out how to add line breaks in a comment – PhishTail Aug 14 '17 at 23:09
  • You need to pass the `Illuminate\Http\Request` instance to your controller action `public function store(Request, $request, Item $item)` – fubar Aug 14 '17 at 23:10
  • It is now passing validation so thanks so much for the help. It's really appreciated! I did remove the comma between in Request $request – PhishTail Aug 14 '17 at 23:20
  • Sorry about the comma, I was typing quickly. Glad it's working. To avoid this, I usually validate my data within my models, so that the mutators have already run, but that's another discussion. – fubar Aug 14 '17 at 23:28
  • I have used RoR so I am used to doing it in the model. The laracasts actually had the rules in the controller which I thought was weird. I'm not quite educated enough to figure out how to do it in the model but I have thought about Form Requests. It looks like something I could figure out with my small knowledge base. What do you think of Form Requests? – PhishTail Aug 15 '17 at 00:26
  • I've just added links to some Gists that show how I implement my model validation. They might give you an idea how to do the same. Very much inspired by RoR. – fubar Aug 15 '17 at 00:45
  • @fubar Your gists have gone offline. Do you still have them anywhere? – MacroMan Aug 20 '19 at 15:51
  • @MacroMan - sorry, it looks like I deleted the gists, and I don't have access to the original source any more. I did write a [blog post](https://klyp.co/insights/five-awesome-code-snippets-enhance-your-next-laravel-project) which included examples a few years ago, which might help. Please excuse the terrible code formatting. – fubar Aug 21 '19 at 00:09
  • good trick `$this->validate($itemNote->toArray(),...` did it for me – G.Mendes Feb 19 '20 at 19:27