WackyStudio / build-an-api-with-laravel

Official Build an API with Laravel repository
122 stars 56 forks source link

Illuminate\Routing\Exceptions\UrlGenerationException Missing required parameters for [Route: books.relationships.authors] [URI: api/v1/books/{book}/relationships/authors]. #31

Closed Saneesh closed 4 years ago

Saneesh commented 4 years ago

I'm getting the following error when I test the relationships: Illuminate\Routing\Exceptions\UrlGenerationException Missing required parameters for [Route: books.relationships.authors] [URI: api/v1/books/{book}/relationships/authors].

Detailed questing is asked below link: https://laracasts.com/discuss/channels/testing/illuminateroutingexceptionsurlgenerationexception-missing-required-parameters-for-route-booksrelationshipsauthors-uri-apiv1booksbookrelationshipsauthors

Laravel : 7.14.1

Regards, Saneesh

ThomasNoergaard commented 4 years ago

Hi Saneesh,

The reason you’re getting the exception is due to changes in the Laravel framework. We wrote the book when the current version of Laravel was 5.8 and in-between one of the later releases of Laravel 6 and Laravel 7, the way Laravel handles route parameters was changed.

Now you have to give the exact name of the route parameter and are not able to use the idkey anymore, when using the route() helper function.

To fix this, you should make the following change in the BookResource class:

class BookResource extends JsonResource {
  /**
   * Transform the resource into an array.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return array
   */
  public function toArray($request) {
    return [
      'id' => (string) $this->id,
      'type' => 'books',
      'attributes' => [
        'title' => $this->title,
        'description' => $this->description,
        'publication_year' => $this->publication_year,
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
      ],
      'relationships' => [
        'authors' => [
          'links' => [
            'self' => route('books.relationships.authors', [
              'book' => $this->id,
            ]),
            'related' => route('books.authors', [
              'book' => $this->id,
            ]),
          ],
          'data' => $this->authors->map(function ($author) {
            return [
              'id' => $author->id,
              'type' => 'authors',
            ];
          }),
        ],
      ],
    ];
  }
}

Notice how the idkey in the route() helper function has been changed to book. The same has to be done in the test:

/**
 * @test
 */
  public function it_returns_a_relationship_to_authors_adhering_to_json_api_specification() {
    $this->withoutExceptionHandling();
    $book = factory(Book::class)->create();

    $authors = factory(Author::class, 3)->create();

    $book->authors()->sync($authors->only('id'));

    $user = factory(User::class)->create();

    Passport::actingAs($user);

    $this->getJson('/api/v1/books/1', [
      'accept' => 'application/vnd.api+json',
      'content-type' => 'application/vnd.api+json',
    ])
      ->assertStatus(200)
      ->assertJson([
        'data' => [
          'id' => '1',
          'type' => 'books',
          'relationships' => [
            'authors' => [
              'links' => [
                'self' => route('books.relationships.authors', [
                  'book' => $book->id,
                ]),
                'related' => route('books.authors', [
                  'book' => $book->id,
                ]),
              ],
              'data' => [
                [
                  'id' => $authors->get(0)->id,
                  'type' => 'authors',
                ],
                [
                  'id' => $authors->get(1)->id,
                  'type' => 'authors',
                ],
              ],
            ],
          ],
        ],
      ]);
  }

We are in the midst of updating the book to Laravel 7 and will be releasing the updated version as soon as possible.

Saneesh commented 4 years ago

@ThomasNoergaard First of all you guys are superb! :-)

Your solution is working! and my quick fix was for all route():

'self' => route('books.relationships.authors', [$book->id]),

Typo in the book: You might be already noticed, many test case function names contain an_book it should be a_book.

ThomasNoergaard commented 4 years ago

Thank you very much, we're glad we could help.

Thank you, we will note that and correct it in the updated version :-)