Laravel – Slugs einrichten

Slugs machen die URLs Sprechender und damit auch SEO-(Freund)licher. Statt domain.de/posts/1 würde man mit domain.de/posts/mein-erster-blog-eintrag arbeiten. Um das in Laravel zu realisieren, bedient man sich in der Regel externer Bibliotheken. Einmal eine von Spartie und eine cviebrock.

Man kann es natürlich aber auch selber machen. Die Bibliotheken sind hier aber vorzuziehen. Weil es ist keine große Logik dahinter und man spart einfach Zeit.

Ich werde hier die Bibliothek eloquent-sluggable von cviebrock nehmen.

1. Schritt:

composer require cviebrock/eloquent-sluggable 

2. Schritt

php artisan vendor:publish --provider="Cviebrock\EloquentSluggable\ServiceProvider"

3. Schritt

Wähle das entsprechende Model welches durch Slugs aufgerufen werden soll. In unserem Beispiel wäre es App\Models\Post.php (Laravel 8) und App\Post (Larvel < 8)

use Cviebrock\EloquentSluggable\Sluggable;
 
 
 class Post extends Model
 {
     use Sluggable;
 
 
     /**
      * Return the sluggable configuration array for this model.
      *
      * @return array
      */
     public function sluggable(): array
     {
         return [
             'slug' => [
                 'source' => 'title'
             ]
         ];
     }
 } 

In der Methode sluggable() definiert ihr was zu Slug gemacht werden soll. Bei Post (Artikeln) macht der Titel Sinn. Deshalb hier source => ‘title’.

So das war es auch fast schon. Ab jetzt werden beim Anlegen eines neuen Artikels der entsprechende Slug erzeugt und im Model mit abgespeichert. Falls ein Blog-Titel sich wiederholen sollte, würde das Sluggable das erkennen und mit einer Nummerierung hintern slug diesen eindeutig machen.

Um die Artikel jetzt auch mit Laravels Route-Model-Binding aufzurufen, müssen wir im Post Model die Methode

 public function getRouteKeyName()
 {
   return 'slug';
 }   

hinzufügen.

Diese Laravel Methode gibt an, den slug statt die id des Models zu nutzen.

Tailwind Purge unter npm run dev laufen lassen

Einer der Hauptgründe warum ich von Bootstrap auf TaiklwindCSS umgestiegen bin, ist der realisierte Traum von relative kleinen CSS Dateien. Statt ein ganzes CSS Framework immer und wieder zu laden, kann man mit TailwindCSS nur den wirklich im Code benötigten Style generieren lassen. Also eine 100% Effizienz, was bedeutet wir sparen Ressourcen. Und zwar Strom und Ladezeit (GCI – GreenCodeIdiology) So kann man CSS Datei Größen unter 40 KB erhalten und zwar für ein ganzes Projekt. Der Vollständigkeitshalber sei hier aber erwähnt, dass man Bootstrap bis zu einem gewissen Grad auch optimieren kann. Aber es kommt meiner Meinung bei Weitem nicht an die TailwindCSS Möglichkeiten herran.

Die Magie die hinter dem steckt, heißt bei Tailwind purge. Einstellungen zum purgen werden in der tailwind.config.js vorgenommen. Ich weiß es ist vielleicht überflüssig zu erwähnen, aber man kann nur purgen, wenn man Tailwind als Package geladen hat und nicht als CDN nutzt. Wie ihr euch TailwindCSS über ein Packagemanager installiert setze ich an dieser Stelle voraus.

Tailwind purged = bereinigt sobald es auf „Production“ geht. Also npm run prod. Bei einem npm run dev wird noch nicht gepurged. Wer das aber gerne möchte, kann es manuell in der tailwind.config.js einstellen.

module.exports = {
  purge: {
    enabled: true,
    content: [
      './src/**/*.html',
      './src/**/*.vue'
    ],
},

Und jetzt mal run dev laufen lassen und ihr werdet sehen, statt der üblichen 3,83 MB werden es erstaunlich weniger sein.

Laravel Blade Components

Componets – Aus VueJS kennen wir es und haben es bereits lieben gelernt. In Laravel können wir im Blade auch sehr dynamisch in Componenten arbeiten. Von Hause gibt uns Laravel Componets mit.

Am Beispiel erklärt

Stellen wir uns vor, wir wollen ein Blog erstellen. In der Übersichtsseite, sollen die Blogartikel untereinander gelistet werden. Wer ohne Componets gerabeitet hatte, wird in einer @foreach Schleife die Artikel ausgelesen haben und innerhalb der Blade Direktive @foreach ein HTML Block befüllt haben. In etwas so:

@foreach ($posts as $post)
  <div>
    <h1>$post->title</h1>
    <p>$post->description</p>
  </div>
@endforeach 

Wir können nun aber auch:

<x-postListing :posts=”$posts”/> 

schreiben.

Jetzt wird die Componente PostListing aufgerufen und die Posts Collection wird mit übergeben. Dafür legen wir die Componente PostListing mal an.

php artisan make:component PostListing 

Laravel Artisan legt uns zwei Dateien an.

1. app/View/Components/PostListing.php

2. views/components/postListing.blade.php

1. app/View/Components/PostListing.php

 use Illuminate\View\Component;
 
 
 class Card extends Component
 {
      * Create a new component instance.
      *
      * @return void
      */
     public function __construct()
     {}
 
 
     /**
      * Get the view / contents that represent the component.
      *
      * @return \Illuminate\Contracts\View\View|\Closure|string
      */
     public function render()
     {
         return view('components.card');
     }
 } 

2. views/components/postListing.blade.php

<div>
     <!-- Knowing is not enough; we must apply. Being willing is not enough; we must do. - Leonardo  
</div>

Nun müssen wir der Componente erklären, dass sie den Parameter posts erhalten wird. Das geschieht im Constructor der Klasse.

public function __construct($post)
{
  $this->post = $post;
} 

Und ab jetzt ist die Varaible ibn der PostListing Blade auch verfügbar. Deshalb können wir die Collection mit einer Foreach durchlaufen lassen. Also fügen wir in unsere Blade ein:

@foreach ($posts as $post)
 {{$post}}
 <x-posts :posts=”$post”/>
@endforeach

Nun können wir das ganze noch weiter gehen. Ich meine damit das wir eine weitere Componente dem ganzen hinzufügen. Und zwar die Componente Card. Jeder Post soll in einer Card im Listing dargestellt werden. Damit würden wir ein Grundstein für eine saubere Blade / View Architektur unser App legen. Zum Beispiel könnten wir mehre Carddesigns besitzen und diese werden im PostLinsting getriggert.

Laravel Resource – Kurz mal erklärt

Nutzen wir zum Beispiel Larvel als Backend und beliefern das Frontend via api Route mit Daten, dann stellt sich manchmal die Frage, welche Daten braucht das Frontend eigentlich? Braucht man wirklich created_at oder updated_at mit auszuliefern?

Sobald man sich so eine oder ähnliche Frage stellt wird es Zeit sich mal mit Laravel Resource auseinander zu setzen. Ich nehme mal stark an, dass wenn ihr diese Zeilen ließt, über die Suchmaschine auf diesen Text gestoßen seid. Also habt ihr euch die Frage mit Sicherheit vor kurzem erst gestellt.

Lange Rede und kein Sinn. Gehen wir mal über zum praktischem Teil. Wir wollen einer Api anfrage nur bestimmte Felder mitliefern. Klassisches Beispiel, wir haben ein Blog. Und im Listing brauchen wir eigentlich nur: Title, Slug, Teaser.

Im Api/PostController unter der index() Funktion steht erstmal nur:

public function index() {
 return Post::all();
} 

Um nur die oben genannten Felder auszuliefern, benötigen wir ein PostResource Klasse. Die erstellen wir mit artisan in der Kommandozeile.

php artisan make:resource PostResource 

Artisan legt mit diesem Befehl eine Datei unter app/Http/Resources ab. Und zwar die PostResource.php. Diese hat eine öffentliche Funktion toArray($request). In der löschen wir die aktuelle Rückgabe : parent::toArray($request); und schreiben ein Array mit den gewünschten Felder die wir dann returnen wollen.

return [
  ‘id‘ => $this→id,
 ‘title‘ => $this→title,
  ‘slug‘ => $this→slug,
 ‘teaser‘ => \Illuminate\Support\Str::limit($this→content, 20),
]; 

Das $this ist das aktuelle PostModel.

Nun wechseln wir wieder in unseren PostController und schreiben die aktuelle index Funktion um.

public function index() {
  return PostResource::collection(Post::all());
}

And viola!

Laravel Spatie Permission Package – Kurz mal erklärt

Die belgische „SoftwareGang“ Spatie haut ein coole Laravel Package nach dem anderen raus. Super nützlich und sehr angenehm zu nutzen. Heute gibt es einen Einstieg in das Package Spatie Permission Package.

I. Einrichten

1. Schritt:

composer require spatie/laravel-permission 

2. Schritt: Permission Service Provider anmelden

Füge dafür den „PermissionServiceProvider“ in der Datei config/app.php ein.

 'providers' => [
     // ...
     Spatie\Permission\PermissionServiceProvider::class,
 ]; 

3. Schritt: Kopiere die migration aus dem Vendor in das Projektverzeichnis

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" 

4. Nun noch den Config cache bereinigen

php artisan config:clear

5. Migration starten

php artisan migrate 

6. Füge den Trait zum User Model hinzu.

 use Spatie\Permission\Traits\HasRoles;

 class User extends Authenticatable
 {
     use HasApiTokens, HasFactory, Notifiable, HasRoles;
 
 

Dadurch haben wir nun Zugriff auf die Spatie Permissions Methoden um dem User Rolen zuzuweisen.

Das war das einrichten und nun gehen wir über zum nutzen.

II. Anwenden

Dafür gehen wir aus, dass wir einen Blog bauen. Hier haben wir unterschiedliche Rollen wie zum Beispiel: Admin, Writer, Reviewer. Der Weiter hat folgende Berechtigung: „edit post“.

Um die Rollen und Berechtigungen zu erstellen, nutzen wir einen Seeder. Den erstellt man in Laravel mit:

php artisan make:seeder RolesSeeder 

In deren run Methode definieren wir unser Model.

 <?php
 namespace Database\Seeders;
 
 
 use Illuminate\Database\Seeder;
 use Spatie\Permission\Models\Role;
 
 
 class RolesSeeder extends Seeder
 {
     /**
      * Run the database seeds.
      *
      * @return void
      */
     public function run()
     {
         $roles = ['admin', 'provider', 'customer'];
 
 
         foreach($roles as $role) {
             Role::create(['name' => $role]);
         }         
     }
 } 

Das Gleiche nun auch für die Berechtigungen:

php artisan make:seeder PermissionsSeeder 
 <?php
 namespace Database\Seeders;
 
 
 use Illuminate\Database\Seeder;
 use Spatie\Permission\Models\Permission;
 
 
 class PermissionsSeeder extends Seeder
 {
     /**
      * Run the database seeds.
      *
      * @return void
      */
     public function run()
     {
         $permissions = ['edit post', 'delete post'];
 
 
         foreach($permissions as $permission) {
             Permission::create(['name' => $permission]);
         }                 
     }
 } 

Jetzt deklarieren wir der DatabaseSeeder Klasse, welche Seeds er „runen“ soll.

<?php
 namespace Database\Seeders;
 
 
 use Illuminate\Database\Seeder;
 
 
 class DatabaseSeeder extends Seeder
 {
     /**
      * Seed the application's database.
      *
      * @return void
      */
     public function run()
     {
         $this->call(RolesSeeder::class);
         $this->call(PermissionsSeeder::class);
 
 
         // create User
         $user = \App\Models\User::create([
             'name' => 'Martin',
             'email' => 'martin@peoplehelper.org',
             'password' => bcrypt('password')
         ]);
 
 
         // asaign user a role as writer
         $user->assignRole('writer');
 
 
         // assign permission to an role (1 = edit post)
         $role = $user->roles[0];
         $role->givePermissionTo(\Spatie\Permission\Models\Permission::find(1));
     }
 } 

Sobald wir das haben können wir mal die Migration durchlaufen lassen:

php artisan db:seed  
// bzw.
php artisan migrate:refresh –seed 

Ein Blick in unsere Datenbank zeigt, dass wir erfolgreich die Role Writer dem User Martin zugewiesen haben.

Für das Wiederrufen einer Berechtigung ($user->revokePermissionTo(‘delete post‘) ) oder das entfernen einer Role vom User ($user->removeRole(‘writer‘)) könnt das ganz gut in der Spatie Doku nachlesen. Das Gleiche gilt für die Konditional Abfragen wie zum Beispiel $user→can(‘delete post‘).

Die „Blade Directives“

Hier kann man mit:

 @can(‘edit post‘)
 ...
 @endcan
 
 
 @role(‘writer‘)
 …
 @endrole
 
 
 @hasrole(‘writer‘)
 ...
 @endhasrole
 
 
 @hasanyrole(Collection || Array )
 @endhasanyrol
 
 
 @hasanyrole(‘writer|admin‘)
 ...
 @endhasanyrole
 
 
 @unlessrole('admin')
 ...
 @end unlessrole 

Logiksecurity statt nur Viewhidding

Ich musste mal mit einem Indischen PHP Entwickler zusammen arbeiten, der die Berechtigungslogiken nur in den Blades abbildete. Im Controller und bzw. oder Serviceklassen hat er es weitestgehend vermieden. Was zu einem Sicherheitsproblem wurde.

Deshalb sein es hier wichtig zu erwähnen, dass nicht zu vergessen. Ein eleganter und einfacher Ansatz wäre hier mit einer Middleware zu arbeiten. Somit kann man die Routes ohne viel Umwege gut vor unberechtigten URL aufrufen schützen.

Alles was wir machen müssen ist:

1: Schritt: Das PermissionRole Package der Route Middleware hinzufügen.

Das passiert in der Kernel.php Datei in der Membervariablen $routeMiddleware. Hier fügen wir dem Array folgende Middlewarepakete hinzu.

protected $routeMiddleware = [
     // ...
     'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
     'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
     'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
 ]; 

Ab diesem Moment können wir in den Routes über Rollen und Berechtigungen schützen.

 Route::group(['middleware' => ['role:admin']], function () {
     //
 });
 
 // bzw.   
 
 Route::get(‘posts/create‘, [\App\Controllers\PostController,])→ ‘create‘])->middleware(['middleware' => ['role:admin']);
 
 
 Route::group(['middleware' => ['permission:publish articles']], function () {
     //
 });
 
 
 Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
     //
 });
 
 
 Route::group(['middleware' => ['role_or_permission:super-admin|edit articles']], function () {
     //
 });
 
 
 Route::group(['middleware' => ['role_or_permission:publish articles']], function () {
     //
 }); 

Wer die routes aus einem Grund nicht schützen kann oder will, kann das im Controller oder in der ServiceKlasse Deiner Anwendung auch vornehmen. Dort lädt man im Konstruktor der jeweiligen Klasse die Middleware und überprüft dann die Userberechtigung bzw. Userrolle.

 public function __construct()
 {
     $this->middleware(['role:admin','permission:publish articles|edit articles']);  
     // oder
     $this->middleware(['role_or_permission:super-admin|edit articles']);
 } 

Das war es auch schon für den kurzen Einstieg. Viel Spaß beim ausprobieren und nutzen!

Rufe eine Parent Methode aus einer Child Componente auf

Wer bei Vue die Componente ansatzweise kleinteilig gliedert, wird schnell auf eine Fragestellung gestossen sein: “Wie kann ich eine Parent Methode aus einer Child Componente aufrufen?” Am Beispiel kurz mal erklärt.

Eure Parent Componente ist eine Artikelliste. Wenn man auf ein Item der liste klickt öffnet sich ein Modal. Dieses Artikel Modal ist die Child Componente und zeigt den einzelnen Artikel an. Der Artikel wird über eine Propetie an die Child übergeben.

import postModal from './PostModalComponent.vue'
export default {
  name: "post-table",   
  components: {       
    postModal   
  },   
  props: ['data'],   
  data() {     
    return {         
      postData: '',         
      showModal: false,     
    }   
  },   
  methods: {       
    openModal(data) {           
      this.postData = data           
      this.showModal = true;       
    },       
    customHandler: function() {
      console.log("works");           
      alert("update");       
    }   
  }
} 

Die Kommunikations von Parent zu Child sind stets Propeties bezigen. Also Daten. Wiederrum ist die Kommunikation von Child zur Parent Componente Event bezogen.

Heißt also, wenn in der Child Componente ein Event (Klick, Hover etc.) passiert, kann die Parent Componente in Kenntniss gesetzt werden. Das geschieht über die Globale $event Variante. In der Childkomponente ist sie in den Methoden mit this.$event anzusprechen. Was darauf hindeutet, dass sie eine vererbte Methode ist.


export default {   
  name: "post-modal",   
  props: {     
    showing:{       
      default:false     
    },     
    data: {       
      type: Object     
    }   
  },   
  data() {     
    return {       
      formValues: {         
        title: this.data.title,         
        content: this.data.content,       
      },     
    }   
  },   
  methods: {     
    async submitHandler (data) {           
      axios         
      .put('/api/posts/' + this.data.slug , data)         
      .then(response => {                     
        if(response.status == 200) {         
          this.$emit('updatePost');             
          console.log('SUCCESS')           
        } else {             
          console.log('ERROR')           
        }         
      })     
    }
  } 
}

Solange es ein so einfaches Componenten Verhältnis ist, dann ist es völlig in Ordnung es so zu machen. Aber wenn es verschachtelte Componente sind und die an verschiedenen Stelle der gesamten Applikation auf ein und dieselbe Source of Truth zurückgreifen, kommen Komplexitätsprobleme hinzu. Heißt dann viele Fehler, entwicklung kommt in Stocken und wird teuer. Daher nutzt man hier gerne sogenannten Event Bus. Kann man sich im übertragendem auch als Bus vorstellen. Die Daten steigen in den Bus ein und fahren an den einzelnen Komponeten vorbei und steigen alle an einer Stelle ab. Diese Stelle nennt man dann den store. Hier gibt es Funktionen wie mutator usw.

Git – Closing a Branch – Was soll das sein?

Wer mit Bitcucket arbeitet kennt den Terminus. Nach einem erfolgreichen Merge auf den Master kann der Branch mit einem Häckchen in einer Checkbox geschlossen werden. Unter den Kollegen ist man sich einig. Branch immer schließen. Bloß warum?

Zuerst sollte erwähnt werden, dass es in Git kein explizietes Branch closen gibt. Man kann einen Branch aber löschen. Und letzendlich ist es das! Man macht es eigentlich nur um eine gewisse Ordnung im Repository zu halten. Stellt Euch vor es gibt am Tag 10 PR Anfragen. Das über mehrere Jahre. Da würde ja eine Menge Branchnamen sein. Der Begriff close hat man sich vielleicht aus der Mercury Welt abgeschaut, wo man Branches hidden kann.

Also immer schön Branch closen nach dem merge auf Master. Und falls ihr das vergesst. Ist auch nicht weiter tragisch.

Laravel Test möglicher Fehler – Unknown formatter “name”

Falls Du gerade Dein Test geschrieben hast und du deinen Test mit php artisan test startest kann es sein, dass Du folgende Fehlermeldung erhalten kannst:

 Unknown formatter "name" at vendor/fakerphp/faker/src/Faker/Generator.php:248 

Was ist passiert?

Wahrscheinlich hast Du in deiner seUp() Methode oder in deiner Testfunktion ein factory eingebaut. Zum Beispiel

$this->user = \App\Models\User::factory()->create(); 

Da Laravel einige Optimierungen vorgenommen hat, kannst Du nicht die Standard TestCase Lib nutzen sondern musst die speziell angepasste Lib nutzen. Also ersetzt du folgendes:

PHPUnit\Framework\TestCase 
// mit 
use Tests\TestCase

Dann sollte es hoffentlich wieder alles grün sein. Happy Testing!

PHP Array Pointer Funktionen

Es gibt eine Reihe sehr nützlicher PHP Array Funktionen, die manch ein Entwickler noch nicht ganz oder garnicht geläufig sind. Und dazu zähle ich mich auch und bin froh über diese – bei einem Codereview – gestolpert zu sein. Die Rede ist von current(), next(), prev(), reset(), end() und each().

Ich stelle jede eben genannte mal kurz vor. Unser Setup ist ein Array:

$namens_array = [
 'Karl',
 'Friedrich',
 'Gustav',
 'Richard',
 'Otto',
 'Jochan'
];  

current()

Die Funktion current() gibt den Wert des aktuellen Elements in einem Array zurück. Jedes Array hat einen internen Zeiger auf sein “aktuelles” Element, der auf das erste in das Array eingefügte Element initialisiert wird.

echo current($names_array);
// Output: Karl 

next()

Die Funktion next() bewegt den internen Zeiger auf das nächste Element im Array und gibt es aus.

echo next($names_array);
// Output: Friedrich 

prev()

Bewegt den internen Zeiger auf das vorherige Element im Array und gibt es aus.

echo current($names_array);
// Output: Karl

echo next($names_array);
// Output: Friedrich

echo prev($names_array);
// Output: Karl 

reset()

Anders als man denkt, wird hier kein Element des Arrays gelöscht. Man bewegt lediglich den internen Zeiger auf das erste Element des Arrays. Nennen wir es den Pointer zurücksetzen.

echo next($names_array);
// Output: Friedrich

echo reset($names_array);
// Output: Karl

end()

Gibt den Wert des aktuellen und des letzten Elements in einem Array aus:

echo end($names_array);
// Output: Jochan

each()

Each ist seit PHP 7.2. veraltet und sollte nicht mehr genutzt werden.

Javascript – Das doppelte Ausrufezeichen

Der ein oder andere hat es vielleicht schon gesehen. In Javascript gibt es das doppelte Ausrufezeichen und nicht jeder kann damit auf anhieb etwas anfangen. Mein erster Gedanke war. Doppelte Verneinung. Ein häufiges Stilelement aus der russischen Sprache. Manche denken das ist ein Operator. Wieder andere denken, dass ist ein Tippfehler.

The double exclamation mark

Man nennt das Teil auch „the double exclamation mark“ und es ist kein Operator. Es ist einfach ein doppelter NOT Operator. Halt ein doppeltes verneinen. Hier mal kurz paar Beispiele:

true === !!true
false === !!false
 
!!0 === false
!!1 === true
!!"" === "" // leerer String ist false
!!null === false // null ist falsch
!!{} === true // das Gleiche wie ist das Objekt leer
!![] === true  // das Gleiche wie ist das Array leer 

Meine Mama ist nicht nicht Deine Mama, lieber Bruder

Man kann das schon so verwenden. Ich aber nutze es kaum in meinem Code. Ist wohl etwas für die russische Kollegen. Oder Menschen die gerne so was sagen: Meine Mama ist nicht nicht Deine Mama, lieber Bruder.

Hier mal Beispiele zu Sinn- bzw- Unsinnhaftigkeit des logischen Doppel-NOT-Operators:

var isUser = !!user_id;
var isUser = (user_id != 0) ? true : false;
var isUser = (user_id != 0); 

SeoTheater Autoren