Kategorien
Laravel

#Kapitel 6: Helper

Laravel beinhaltet eine Vielzahl von Hilfsfunktionen. Die Dokumentation dazu ist gut. Es gibt Helper für Arrays, Strings, URLs und so etliches mehr. Bevor man also auf den Gedanken kommt: „Hey, da könnte ich mir doch eine kleine Hilfsfunktion schreiben“, sollte man erstmal einen Blick auf die bestehenden Hilfsfunktionen werfen.

Sollte man jedoch doch eine Funktion benötigen, stellt sich die Frage wie dies zu implementieren ist. Ich habe auf laravel-news.de ein Artikel gefunden. Das Prinzip ist einfach. Es wird an beliebiger Stelle eine PHP-Datei erstellt, dann mittels composer das Autoloading angepasst und anschließend wird der Autoloader neu erstellt (composer dump-autoload). Im Artikel werden nur Funktionen definiert, die vorher mittels function_exists geprüft werden.

Ich will mir das etwas genauer anschauen. So ganz verstanden habe ich es noch nicht, warum es Aufrufe über Klassen z. B. Str::lower($str) und reine Funktionsaufrufe gibt z. B. blank($value).

Also werfen wir mal einen Blick in das Framework.

Wo sind meine Helfer?

Die use-Anweisung in der Dokumentation verrät das z. B. am Beispiel der Str-Helper:

use Illuminate\Support\Str;

Nun stellt sich die Frage, wie die Helper geladen werden (autoload). Ein Blick in die composer.json liefert den ersten Hinweis:

"scripts" : {
         "post-autoload-dump" : [
             "Illuminate\Foundation\ComposerScripts::postAutoloadDump",
             "@php artisan package:discover --ansi"
         ],
         "post-root-package-install" : "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
         "post-create-project-cmd" : "@php artisan key:generate --ansi"
     }

Der Aufruf von ComposerScripts::postAutoloadDump wird vor dem eigentlichen erstellen der Autoload-Dateien durch Composer aufgerufen. Die Funktion schauen wir uns gleich mal an:

/**
  * Handle the post-autoload-dump Composer event.
  *
  * @param  \Composer\Script\Event  $event
  * @return void
  */
  public static function postAutoloadDump(Event $event)
  {
         require_once $event->getComposer()->
           getConfig()->get('vendor-dir').'/autoload.php';
         static::clearCompiled(); 
  }

Es wird nichts weiteres gemacht als die autoload.php aus dem vendor-Verzeichnis einzubinden. Wir befinden uns da schon in der Composer-Autoload-Umwelt. Letztendlich führt uns das zu folgendem Eintrgen in der autoload_classmap.php und autoload_static.php:

'Illuminate\Support\Str' => $vendorDir . '/laravel/framework/src/Illuminate/Support/Str.php',

Na gut, also noch ein Blick in die composer.json von Laravel:

{
  "name" : "laravel/framework",
  // ...
  "replace" : {
    // ...
    "illuminate/support" : "self.version",
    // ...
},
// ...
"autoload" : {
  "files" : [
    "src/Illuminate/Collections/helpers.php",
    "src/Illuminate/Events/functions.php",
    "src/Illuminate/Foundation/helpers.php",
    "src/Illuminate/Support/helpers.php"
  ],
"psr-4" : {
  "Illuminate\" : "src/Illuminate/",
  "Illuminate\Support\" : [
  "src/Illuminate/Macroable/",
  "src/Illuminate/Collections/"
]
// ...

Neben dem Einbinden des Packages werden auch einige Helper direkt eingebunden. Alles deutet darauf hin, dass es kein Autoloading zur Laufzeit gibt.

Der letzte Blick in die composer-Datei des Support-Package bringt keine Überraschung:

// ...
"autoload": {
         "psr-4": {
             "Illuminate\Support\": ""
         },
         "files": [
             "helpers.php"
         ]
     },
// ...

Fazit

Meine Empfehlung ist somit, dass man sich am Framework „Standard“ zu orientieren sollte.

Erstellen eines Ordners Support. Funktionen in die Datei helpers.php (Kleingeschrieben), Methoden in beliebige andere Dateien z. B. Test.php (Großgeschrieben):

Anschließend die composer.json für das Autoloading anpassen:

  "autoload" : {
         "files" : [
             "app/Support/helpers.php"
         ],
         "psr-4" : {
       "App\" : "app/",
       "App\Support\": "app/support/",
       //...
     }
  },

Nachdem der Autoload neu erstellt wurde, stehen die Helper zur Verfügung (composer dump-autoload).

Update 1: In Laravel 8 haben meine Tests ergeben, dass die Anpassung der composer.json nicht notwendig ist. Durch das PSR-4 Autoloading wird das berücksichtigt. Somit empfehle ich auf Function-Helper zu verzichten und nur Methoden zu verwenden.

Beispiel:

 namespace App\Support;

 class Test
 {
   /**
   A crazy function
   */
   public static function crazy($str)
   {
     return "I'm going $str";
   }
 } 

Damit dies auch in Blade-Templates verwendet werden kann, muss man einen Alias in der app.php anlegen:

'aliases' => [
  //...    
  'Test' => App\Support\Test::class, 
]

Dann man das folgendermaßen verwenden:

{{ Test::crazy("very crazy!") }}

Alternativen?

Ja, die gibt es.

Alternative 1: Einen ServiceProvider nutzen

Dazu muss man einen ServiceProvider erstellen:

php artisan make:provider HelperServiceProvider

In diesem kann man alles anstellen was man möchte. Wenn wir die obiges Beispiel verwenden, dann schiebe ich das erstmal in einen anderen Ordner, einfach zu demonstrationswecken:

Nun können wir die register()-Methode des HelperServiceProviders anpassen:

public function register()
{
  foreach (glob(app_path() . '/../some_other_folder/Support/*.php') as $file) {
         require_once($file);
  }
}

Auch das funktioniert.

Alternative 2: 3rd-Party Erweiterungen nutzen

Aufgrund der Einfachheit halte ich das persönlich für keine gute Alternative, aber man kann das natürlich machen. Ein Beispiel ist z. B. auf GitHub, siehe hier.