Tinkering with Controller Methods

To test a method show()as part of a controller in PHP Artisan Tinker you have to make sure the controller is loaded and or the modal in use if the case is as well. It can be rather daunting at first. Certainly if you are like me and just want to barge in, get callback data, variable data and more real data quick.

Show Method

The method in question I was working with and tinkering with in PHP Artisan Tinker is

public function show(Download $download)
{
    $extension = pathinfo(storage_path("app/{$download->path}"), PATHINFO_EXTENSION);

    return response()->download(storage_path("app/{$download->path}"), "$download->name.{$extension}", [
        'Content-Type' => $download->mime,
    ]);
}

It is a method to show downloads added in a response. A little confusing as well as you would think it would perhaps return a view here, but for that we already use an index() method. It however returns a response with file name, extension as well as mime type.

Dependency Injection

And as you can see it has the Download Modal injected using the Laravel Container for dependency injection as parameters:

public function show(Download $download)

And to work with that and not get errors like:

>>> $controller = app()->make('App\Http\Controllers\Admin\DownloadsController');
=> App\Http\Controllers\Admin\DownloadsController {#5043}
>>> app()->call([$controller, 'show'], ['id' => 1]);
Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException with message 'The file "/Users/jasper/code/domain.com/valet/storage/app/" does not exist'

you need to set things up properly.

App Method

The usage of app()[laravel docs] to work with the Laravel Service Container is good and the way a call is made post storing a new controller instance in a variable too.

Make Methods

Make method make() is used to resolve a new instance from the container. You can feed it the class or interface you need. See https://laravel.com/docs/master/container#the-make-method .

So we used app()->make()to get a new controller instance and then we called upon it.

But that is where things get stuck. It cannot load a file and is not really getting database data that has been injected

Set Needed Classes

So this roadblock means you need to run the following in PHP Artisan Tinker REPEL to actually get some results from this method properly. First you need to set the Controller and modal you will be using. In our case DownloadsController and Download Modal

use App\Http\Controllers\Admin\DownloadsController;
use App\Models\Download;

Variable to load first table item

The next step now that we have preloaded these classes including the Download model class is to set a new modal instance to load the record you would like to work with

$download = Download::first();

We pick the first row and in our case only record we have. We use the double colon because we are accessing static properties of a class.

Static Types in PHP

The Scope Resolution Operator (also called Paamayim Nekudotayim) or in simpler terms, the double colon, is a token that allows access to static, constant, and overridden properties or methods of a class.

Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. php docs

Laravel Collections Method First

With first() (docs link) we return the first element in the collection. And Eloquent queries and so Laravel Models always return as collection instances so they can be worked with using collection methods such as first().

A property declared as static cannot be accessed with an instantiated class object (though a static method can). php docs

First Method Details

So that would mean that first() is a static method. And digging deeper and finding it at vendor/laravel/framework/src/Illuminate/Collections/Arr.php it is static:

/**
* Return the first element in an array passing a given truth test.
*
* @param  iterable  $array
* @param  callable|null  $callback
* @param  mixed  $default
* @return mixed
*/
public static function first($array, callable $callback = null, $default = null)
{
  if (is_null($callback)) {
      if (empty($array)) {
          return value($default);
      }

      foreach ($array as $item) {
          return $item;
      }
  }

  foreach ($array as $key => $value) {
      if ($callback($value, $key)) {
          return $value;
      }
  }

  return value($default);
}

Note 1 Eloquent is Laravel’s Database ORM (Object Relational Mapper) where Eloquent models are the query builders

Note 2 The results of Eloquent queries are always returned as Collection instances. The Eloquent Collection class extends Laravel’s base Illuminate\Support\Collection class, which provides a variety of helpful methods for interacting with data collections. For example, the reject method may be used to remove models from a collection based on the results of an invoked closure

Note 3 Since all of Laravel’s collections implement PHP’s iterable interfaces, you may loop over collections as if they were an array. See PHP Doc and this SO thread:

Laravel’s Collection class implements the following interfaces:

ArrayAccess, ArrayableInterface, Countable, IteratorAggregate, JsonableInterface

Note 4 Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the collection documentation.

This method first() based on collection array is loaded as a new collection in vendor/laravel/framework/src/Illuminate/Collections/Collection.php using:

public function first(callable $callback = null, $default = null)
{
    return Arr::first($this->items, $callback, $default);
}

And because it is loaded there it can be used on each Eloquent query being a new collection class instance.

New Controller Instance

And then call a new controller instance

$controller = app()->make('App\Http\Controllers\Admin\DownloadsController');

This will then allow you to get feedback on a show method in use in our case:

>>> use App\Http\Controllers\Admin\DownloadsController;
>>> use App\Models\Download;
>>> $download = Download::first();
=> App\Models\Download {#5093
     id: 1,
     path: "downloads/HmBTtPJgeV0f2LNCjXW8ZeBkP3xvgI6DCIt8i5tp.pdf",
     name: "Grids",
     mime: "application/pdf",
     size: 37480.0,
     active: 1,
     total_downloads: 0,
     created_at: "2020-07-13 04:51:22",
     updated_at: "2020-07-13 04:51:22",
   }
>>> $controller = app()->make('App\Http\Controllers\Admin\DownloadsController');
=> App\Http\Controllers\Admin\DownloadsController {#5105}
>>> app()->call([$controller, 'show'], ['download' => $download])
=> Symfony\Component\HttpFoundation\BinaryFileResponse {#5136
     +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {#487},
   }

Props Devin Gray at Laracasts for getting this far and explaining things!

So to have this line

app()->call([$controller, 'show'], ['download' => $download])

to work you need…. to feed REPEL with more data on the modal object as well as the controller.

Only then you have a binary file response and a Response Headerbag. What do those mean then?

Symfony Binary File Response & Headerbag

So what is the Binary File Response? Well for that you can check the code or even better the Symfony documentation. Here some elements on it that stood out:

The BinaryFileResponse will automatically handle Range and If-Range headers from the request. It also supports X-Sendfile (see for nginx and Apache).

With the BinaryFileResponse, you can still set the Content-Type of the sent file, or change its Content-Disposition:

https://symfony.com/doc/current/components/http_foundation.html#component-http-foundation-serving-files

On the same page there is a bit of information on the Response Header Bag as well but minimal and using a code example:

$response->setContent('Hello World');
 // the headers public attribute is a ResponseHeaderBag
 $response->headers->set('Content-Type', 'text/plain');
 $response->setStatusCode(Response::HTTP_NOT_FOUND);

It states the public header attributes are the response header bag.

Jasper Frumau

Jasper has been working with web frameworks and applications such as Laravel, Magento and his favorite CMS WordPress including Roots Trellis and Sage for more than a decade. He helps customers with web design and online marketing. Services provided are web design, ecommerce, SEO, content marketing. When Jasper is not coding, marketing a website, reading about the web or dreaming the internet of things he plays with his son, travels or run a few blocks.