Warning: The magic method SFML_Singleton::__wakeup() must have public visibility in /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php on line 72

Warning: Cannot modify header information - headers already sent by (output started at /home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php:72) in /home/public/wp-includes/feed-rss2.php on line 8
laravel – Thoughts, etc. https://www.munderwood.ca Tracking the occasional random walk Wed, 10 Feb 2021 21:26:34 +0000 en-CA hourly 1 https://wordpress.org/?v=5.7.2 https://www.munderwood.ca/wp-content/uploads/2016/03/photo-150x150.jpg laravel – Thoughts, etc. https://www.munderwood.ca 32 32 JSON responses as PHP arrays in Laravel 8 tests https://www.munderwood.ca/index.php/2021/02/10/json-responses-as-php-arrays-in-laravel-8-tests/ https://www.munderwood.ca/index.php/2021/02/10/json-responses-as-php-arrays-in-laravel-8-tests/#respond Wed, 10 Feb 2021 21:26:33 +0000 https://www.munderwood.ca/?p=305 [Read more...]]]> I’m in the process of upgrading an API code base that has been in production since Laravel 5, and continues to evolve. The bulk of the update to version 8 went smoothly, but one breaking change that I couldn’t find documented anywhere held me up for some time this afternoon.

A great many of the tests use a pattern of getting a response from an endpoint, running $data = $response->decodeResponseJson(), and then making assertions against the resulting PHP array in $data. Unfortunately, in Laravel 8 the return type is now \Illuminate\Testing\AssertableJsonString, and I kept running into errors such as:

Argument #2 of PHPUnit\Framework\Assert::assertArrayHasKey() must be an array or ArrayAccess

I made various attempts to convert the output of decodeResponseJson into an array (and being confused as to why it didn’t work, since AssertableJsonString implements ArrayAccess), but in the end the solution was simple.

Instead of refactoring every existing test to use Laravel’s new JSON-based assertions, I merely had to change each call to decodeResponseJson to just json. That is, switching to

$data = $response->json();

was all it took.

]]>
https://www.munderwood.ca/index.php/2021/02/10/json-responses-as-php-arrays-in-laravel-8-tests/feed/ 0
Getting a Faker instance in Laravel’s tinker environment https://www.munderwood.ca/index.php/2018/05/03/getting-a-faker-instance-in-laravels-tinker-environment/ https://www.munderwood.ca/index.php/2018/05/03/getting-a-faker-instance-in-laravels-tinker-environment/#comments Thu, 03 May 2018 14:47:51 +0000 http://www.munderwood.ca/?p=250 I sometimes find that it would be useful to use  artisan tinker to play with the sorts of data that Faker will generate in a Laravel factory. Unfortunately I don’t do it often enough to always remember the correct way to do it, since the obvious  new Faker\Generator doesn’t work. So for future reference, what you need is:

>>> $faker = Faker\Factory::create();

 

]]>
https://www.munderwood.ca/index.php/2018/05/03/getting-a-faker-instance-in-laravels-tinker-environment/feed/ 3
Testing routes with Laravel-Localization https://www.munderwood.ca/index.php/2016/03/16/testing-routes-with-laravel-localization/ https://www.munderwood.ca/index.php/2016/03/16/testing-routes-with-laravel-localization/#comments Thu, 17 Mar 2016 00:55:10 +0000 http://www.munderwood.ca/?p=79 [Read more...]]]> I recently added the Laravel-Localization package (with Laravel 5.2) to a project I’m working on. Initial setup went smoothly, and I was impressed with how well things worked essentially out of the box.

Then I tried to test my routes.

I had previously made a test case that ran through all of my public routes and asserted that each one returned HTTP status code 200 OK. Suddenly this was failing on every route I tried, saying that 404 Not Found was being returned instead.

There was 1 failure:

1) AllRoutesOkTest::testGetRoutes
A request to [http://localhost/en] failed. Received status code [404].

The route worked in the browser. It returned the correct result using curl on the vagrant box. But it refused to work in the test. I had the application’s default locale set to ‘en’, and every indication was that it worked perfectly everywhere except in the tests. I pared the test right down:

public function testMinimalExample()
{
    $this->visit('/')
         ->assertResponseOk();
}

The error was the same. The request was correctly updated from / to /en based on the default locale, but apparently Laravel insisted on returning 404 to the test. I would have understood if the status code were 301 or 302, since I’m using the LaravelLocalizationRedirectFilter middleware and expect there to be a redirect. But why would the route simply disappear?

To make a long debugging session short, it turns out that while LaravelLocalizationRedirectFilter middleware correctly leads any browser to make a second request for the new URL, the testing framework tries to follow redirects within the routes available to the same initial request object.

Standard operation of Laravel-Localization is to set up a route group with a prefix value of LaravelLocalization::setLocale(). When the browser visits the homepage initially, this function returns null so Route::get('/', ...) is matched, the middleware is invoked, and the browser is redirected to /en. The browser then invokes a second request, this time to /en as directed, which means setLocale() is called again, this time returning 'en'. So Route::get('/', ...) is now prefixed by 'en',  the requested route is matched, and everything works correctly.

In the test however, there is only ever a single request object, in which the prefix defined by setLocale() is simply ''. This matches the first time, the server runs the middleware and sends a redirect, but instead of making a second request, the testing framework tries to follow the redirect by resolving the new route against the RouteCollection in the original Request object. Since it was created with a prefix of '', any route prefixed by 'en' won’t exist.


So, how do we solve this? I’ve found two interim solutions so far, though I plan to keep looking another day.

Option 1 is to disable the LaravelLocalizationRedirectFilter middleware. This means that requests to un-locale-prefixed routes will work just fine, but won’t be redirected to their prefixed versions, resulting in two different URLs for each resource in the default language.

Option 2 is to set 'hideDefaultLocaleInURL' => true in the laravellocalization.php config file. This results in the redirection of all URLs prefixed with the default locale to unprefixed ones—/en becomes /, /en/users becomes /users, and so on. This is perhaps preferable to Option 1 from an SEO standpoint, but comes with the caveat that the Accept-Language header of any user’s browser will now be ignored.

Likely some other options exist that involve changing the requested routes in the test cases instead.

]]>
https://www.munderwood.ca/index.php/2016/03/16/testing-routes-with-laravel-localization/feed/ 7
Laravel 5.1 “intermediate quickstart” notes https://www.munderwood.ca/index.php/2015/12/16/laravel-5-1-intermediate-quickstart-notes/ https://www.munderwood.ca/index.php/2015/12/16/laravel-5-1-intermediate-quickstart-notes/#respond Wed, 16 Dec 2015 08:09:17 +0000 http://www.munderwood.ca/?p=60 [Read more...]]]> I just ran through Laravel’s intermediate quickstart tutorial. On the whole it was rather well put together and worked with as little or as much copying and pasting as you felt like using. Here I’m just recording a few notes for future reference about non-obvious things I encountered.

Error messages for simple typos

One interesting thing to note is that using curly braces instead of round brackets in your blade templates can lead to not-obviously-related error messages. For instance, my first attempt at a template began with

// Incorrect way to extend a layout:
// Note the use of {} instead of ()
@extends{'layout.php'} // DO NOT DO THIS!

instead of the correct

// Correct way to extend a template
@extends('layout.app')

While obviously this is a simple typo that is entirely my own fault, the resulting error message,

FatalErrorException in fba8cddd62bd85dee11955629f018a61 line 24:
syntax error, unexpected ‘,’

was not particularly enlightening, and I spent rather longer than I would have liked before noticing my error.

Importing models and policies for authorisation

The tutorial seems to leave out two important lines when it comes to authorising the deletion of tasks via policy. Specifically, when the tutorial gets to the point of adding the new policy to app/Providers/AuthServiceProvider.php ,

protected $policies = [
    Task::class => TaskPolicy::class,
];

it fails to mention that this won’t work until the lines

// app/Providers/AuthServiceProvider.php

// ...

use App\Task;
use App\Policies\TaskPolicy;

// ...

are also added to the file.

Redirecting on login

Finally, if you create your own files from scratch for the tutorial instead of cloning their repo, then registering and logging in will appear to result in an error,

NotFoundHttpException in RouteCollection.php line 161:

This can be surprising at first, and might make you think there actually is an error in your code, but of course it is essentially just a 404 Not Found error—by default you are redirected to /home , which hasn’t been created. One option would of course be to create home view. Another, which is what the Laravel repo does, is to override the redirection destination in app/Http/Controllers/Auth/AuthController.php  and point it at  /tasks instead.

To do so, simply set the  $redirectTo property to the desired path, for example

// app/Http/Controllers/Auth/AuthController.php

class AuthController extends Controller
{
    // ...

    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    protected $redirectTo = '/tasks';

    // ...
}

 

]]>
https://www.munderwood.ca/index.php/2015/12/16/laravel-5-1-intermediate-quickstart-notes/feed/ 0