1

I'm developing a Help Centre for a site and I've asked the question over at the Laracast Forums to which I haven't had a solution from yet unfortunately, here us the routes/url paths for it:

routes.php

Route::resource('admin/help-centre/category', 'AdminHelpCentreCategoryController');
Route::resource('admin/help-centre/category/{category}/section', 'AdminHelpCentreSectionController');
Route::resource('admin/help-centre/category/{category}/section/{section}/article', 'AdminHelpCentreArticleController');

Category and Section validation are working as expected, but for some reason even though it's the same logic, as far as I can tell, the Article validation doesn't seem to work as expected. Here are my validation rules (I've removed the excess and just left the title field):

HelpCentreCategoryRequest.php

/* Insert ------------------------------------- */
    $rules = [
        'title' => 'required|min:3|unique:help_centre_categories',
    ];

/* Update ------------------------------------- */
    if ($method == 'PATCH') {

        $rules = [
            'title' => 'required|min:3|unique:help_centre_categories,title,'.$this->category->id,
        ];

    }

HelpCentreSectionRequest.php

/* Insert ------------------------------------- */
    $rules = [
        'title' => 'required|min:3|unique:help_centre_sections,title,NULL,id,category_id,'.$this->category->id,
    ];


/* Update ------------------------------------- */
    if ($method == 'PATCH') {

        $rules = [
            'title' => 'required|min:3|unique:help_centre_sections,title,'.$this->section->id.',id,category_id,'.$this->category->id,
        ];

    }

I'll explain just to clarify... for Categories, on INSERT it checks to see if that title already exists, if it does, it doesn't validate. UPDATE does exactly the same except it ignores itself in the check so it can still validate if the title hasn't been changed. So I entered 2 Categories, one called Category 1 and the other Category 2.

Same goes for the Section request, except it only checks for Sections which are in the specified Category you're creating it in. I've tested this out and it works. I can insert foo and bar into Category 1 and foo and bar into Category 2, but if I try to add another foo into either Category 1 or Category 2, it doesn't validate cause it's a duplicate, which is correct. But if I try to update foo in Category 1 to bar it won't allow it, and also if I try to update foo without making any changes, it ignores itself (row) allowing it to pass and not throw a validation error. So everything works as it should for this.

Now for my Article validation, I've used the exact same process/query validation as the Section validation, except changing table/column/variables to suit, like this:

HelpCentreArticleRequest.php

/* Insert ------------------------------------- */
    $rules = [
        'title' => 'required|min:3|unique:help_centre_articles,title,NULL,id,section_id,'.$this->section->id,
    ];


/* Update ------------------------------------- */
    if ($method == 'PATCH') {

        $rules = [
            'title' => 'required|min:3|unique:help_centre_articles,title,'.$this->article->id.',id,section_id,'.$this->section->id,
        ];

    }

This should do exactly the same as HelpCentreSectionRequest.php, so allow only one copy of an Article title in each Section of a Category, while allowing an Article of the same title but in a different Section of a Category. That part works fine, however if I insert foo and bar in Category 1 / Section 1, and then update foo to bar it allows the change and to me, it shouldn't since bar already exists in Category 1 / Section 1.

I'm obviously wrong though or else it would work as I expected it too ha. But I just can't see what the problem is?

Here is the database schema just to save any hassle and can confirm table/column names match (again with foreign keys and extra columns removed):

Schema::create('help_centre_categories', function(Blueprint $table)
{
    $table->increments('id')->unsigned();
    $table->string('title');
});

Schema::create('help_centre_sections', function(Blueprint $table)
{
    $table->increments('id')->unsigned();
    $table->integer('category_id')->unsigned();
    $table->string('title');
});

Schema::create('help_centre_articles', function(Blueprint $table)
{
    $table->increments('id')->unsigned();
    $table->integer('category_id')->unsigned();
    $table->integer('section_id')->unsigned();
    $table->string('title');
});

I've also checked the if statement in the Article request is definitely firing which was mentioned in the Laracast Forum link at the top. You can also find the Valiation docs here, but for easy reading, this is the main section I am using:

Adding Additional Where Clauses

You may also specify more conditions that will be added as "where" clauses to the query:

'email' => 'unique:users,email_address,NULL,id,account_id,1'

In the rule above, only rows with an account_id of 1 would be included in the unique check.
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
no.
  • 2,356
  • 3
  • 27
  • 42

1 Answers1

1

Well, first of all your code is hard to analyze, If I were you, I would use array syntax for that:

   $rules = [
        'title' => ['required'. 'min:3'],
   ];
   $uniqueRule = 'unique:help_centre_categories';

   /* Update ------------------------------------- */
    if ($method == 'PATCH') {
       $uniqueRule.='title,'.$this->category->id
    }
    $rules['title'][] = $uniqueRule;

The second thing is the $method variable. I don't know where it comes from, but assuming it holds the correct value, you should probably change your condition this way:

if (strtoupper($method) == 'PATCH' || strtoupper($method) == 'PUT') {
 ...
}

Last thing, if I try to use one Request class both for store and update, I usually do it this way:

$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
 // now you know it's update
}

If your method does not work, you could try this above.

EDIT

Inside your Request object you can use getMethod() method, for example:

public function rules() 
{
   $method = $this->getMethod();
   if ($method == 'PUT' || $method == 'PATCH') {
        // now you do what you want for update
   }

   // rest of code goes here
}

For update you should verify if $method is PUT or PATCH

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
  • How is my code hard to analyze? I don't see how your array method differs that much from mine, the outcome is exactly the same? It's just written differently? I did forget to mention that `$method` is getting it's value from `Request::method()`, sorry about that. I'll try the `strtoupper` and adding `PUT` into the statement too. The segment approach doesn't seem very practical to me either? Unless I'm missing something? I know the `$method` statement works as it works for both Category and Section requests, I believe the problem lies in the `'title' => ''` line of the Article request file. – no. Mar 22 '15 at 18:59
  • @Joe It's only better because you can easier add new conditions or even remove them. As I showed it's better to group repeating conditions for create and update and for update just modify the rule than repeat all 3 rules both for create and update. This is the only thing why it could be better. Probably in your case the problem is missing PUT and maybe letter case of PATCH/PUT – Marcin Nabiałek Mar 22 '15 at 19:13
  • That makes sense then with your method, I'll take that approach. I also tried converting the strings to uppercase and also adding `put` to the statement, but it still didn't work. I also found `Request::isMethod()` in the Laravel docs which seems a more guaranteed approach but it still isn't working by trying `if (Request::isMethod('put') || Request::isMethod('patch')) {`, I've tried both lowercase and uppercase too. So rather confusing as to why I can't get it to work as expected. Thank you for your help so far though Marcin! – no. Mar 23 '15 at 16:23