Laravel

Inkludert i Laravel

Artisan

Laravel benytter et CLI som heter Artisan. Det bruker vi til å:

  • opprette controllers og modeller
  • lage migrasjonsfiler og til å kjøre migrasjoner mot databasen
  • lage events, jobs, form requester etc
  • vise ruting
  • session-kommandoer
  • kjøre Tinker
  • lage custom kommandoer.

Eksempelkommandoer er:

$ php artisan list

Viser alle tilgjengelige kommandoer

$ php artisan help migrate

Viser informasjon om en spesifikk kommando

$ php artisan make:controller TodosController

Kontrollere skal være i flertall

$ php artisan make:model Todo -m

Modeller skal være i entall

$ php artisan make:migration add_todos_to_db -table=todos

Lage en egen migrasjon

$ php artisan migrate

Kjører alle migrasjoner som ikke er kjørt enda

$ php artisan tinker

Gjør at du kan interagere med databasen fra kommandolinja

Eloquent

Laravel benytter Eloquent som ORM. Det gjør at vi kan jobbe med databasen og modeller veldig enkelt.

Eksempel:

Use App\Todo;
$todo = new Todo;
$todo->title = ‘Clean windows’;
$todo->save();

Blade Template Engine

Brukes til views. Du kan fortsatt bruke php-tags inni malene.

Eksempel:

resources/views/layouts/app.blade.php

<html>
  <head>
    <title>App Name – @yield(‘title’)</title>
  </head>
  <body>
    @section(‘sidebar’)
      This is the master sidebar
    @show

    <div classcontainer«>
      @yield(‘content‘)
    </div>
  </body>
</html>

resources/views/child.blade.php

@extends(‘layouts.app’)

@section(‘title’, ‘Page Title’)

@section(‘sidebar’)
  @parent
  <p>This is appended to the master sidebar.</p>
@endsection

@section(‘content’)
  <p>This is my body content</p>
@endsection

Installasjon / Quickstart

Vi installererer Larvel via Composer, så last først ned det, og installer:

https://getcomposer.org

Du må ha en server stack på maskinen din (xampp, mampp, etc. Hvertfall PHP og mysql)

Med terminalen, gå til htdocs / www / public-mappa, og kjør:

$ composer create-project laravel-crud
$ composer install

Obs: Jeg hadde litt trøbbel med den siste, og måtte installere noen PHP-avhengigheter før den ville virke:

$ sudo apt-get install php7.2-zip
$ sudo apt-get install php-mbstring -y
$ sudo apt-get install php-xml -y

Anbefaler å gå til config/app.php, finn APP_DEBUG, og sett den til true, først som sist.

Jeg måtte deretter legge til en laravel encryption-nøkkel ved å kjøre kommandoen:

$ php artisan key:generate

Til sist satte jeg opp en virtualhost for etc/apache2/sites-enabled/laravel-crud.conf:

<VirtualHost *:80>
    DocumentRoot «C:/Tingogtang/Development/Back-end/PHP/www/laravel-crud/public/»
    ServerName laravel-crud.test
    ServerAlias *.laravel-crud.test
    <Directory «C:/Tingogtang/Development/Back-end/PHP/www/laravel-crud/public/»>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Da kunne jeg omsider komme inn på laravel-crud.test i nettleseren.

Routers

Ruting ligger passende nok i routes-mappa. Som standard viser laravel sin landingsside, men det kan vi straks gjøre noe med.

Gå til routes/web.php, og kommenter ut return-linja.

Legg deretter til

return ‘Hello World’;

Hvis du nå refresher browseren til laravel-crud.test, vil du få opp teksten Hello World.


Hadde vi lagt til noe i det første argumentet i Route::get(), f.eks hello, så ville teksten dukket opp på /hello:

Route::get(‘/hello’, function() {
  return ‘Hello World’;
});

Du kan bruke andre ting enn get-requester, slik som: Route::post(), Route::delete(), osv. Det er med andre ord ganske rett frem å lage et REST-api.

Inni Views-mappa bør du ha en mappestruktur, for å holde ting ryddig. I første sted kan du lage en mappe for pages. Inni denne lager vi en about side (about.blade.php). Hvis vi da skal lage en route som bruker denne fila gjør vi slik:

Route::get(‘/about’, function() {
  return view(‘pages.about’);
})

Du kan bruke punktum eller foroverslash når du kaller et view, men punktum er konvensjonen. Den leter da i resources/views-mappa, finner pages-mappa, og finner about-fila.

Hvis du skal sende inn en get-parameter, som f.eks en bruker-ID, gjøres det ved å legge til krøllparantes på slutten av ruta, og legger til id’en som et argument for callback funksjonen:

route::get(‘/users/{id}’, function($id) {
  return ‘this is user $id’
});

Du vil da kunne gå til laravel-crud.test/users/22, og få opp det tallet. Du kan kommentere ut denne for nå.

Vi ønsker vanligvis aldri å returnere et view direkte i routeren. Vi refererer heller til en funksjon i en controller, som tar seg av hva som vises i nettleseren.

Til dette kan vi hoppe tilbake til terminalen, og bruke artisan:

$ php artisan make:controller PagesController

Den lager da en klasse i app/Http/Controllers som heter PagesController.php.

Det er da en klasse som arver fra Controller. La oss fjerne kommentaren og lage en metode:

public function index() {
  return ‘INDEX’;
}

Hopp tilbake til routeren vår (web.php), og gå til get-requesten som går til rota (/).

Fjern funksjons-argumentet, og skriv inn navnet på kontrolleren vår, legg til en @, og navnet på klassemetoden vi ønsker å kjøre:

Route::get(‘/’, ‘PagesController@index’);

Så hvis du nå lagrer og oppdaterer browseren på laravel-crud.test, så vil du se teksten INDEX.


Det vi ønsker å levere fra controlleren er et view, ikke ren tekst, så vi kan hoppe tilbake til controlleren, og bytte innholdet i metoden med:

return view(‘pages.index’);

Laravel leter da etter resources/views/pages/index.blade.php, så la oss opprette den. Kopier inn innholdet fra welcome.blade.php inn i index.blade.php. Fjern alt inni body, style-tag’en og font-importen.

Du kan gå til .env fila, og endre navnet på app’en din (jeg endret til laravel-crud). Deretter kan du gå tilbake til index.blade.php, og endre innholdet i title-tag’en med:

{{config(‘app.name’, ‘laravel-crud’)}}

I body kan du legge til

<h1>Welcome to Laravel</h1>
<p>This is a Laravel application</p>

Hopp tilbake til nettleseren, og gå til rotområdet. Teksten dukker forhåpentligvis opp.

Views og Controllers

Lag en about.blade.php, og en services.blade.php, inni resources/views/pages.
Kopier inn innholdet fra index, men bytt ut H1-tag’en til henholdsvis About og Services, og bytt ut p-tag’en med “This is the […] page”, altså about og services.

Så går vi til PagesController.php, og legger til disse:

public function about() {
  return view(‘pages.about’);
}

public function service() {
  return view(‘pages.services’);
}

Til slutt må vi legge til de nye rutene i routes/web.php:

Route::get(‘/’, ‘PagesController@index’);
Route::get(‘/about’, ‘PagesController@about);
Route::get(‘/services’, ‘PagesController@services’);


Og da skal alle rutene fungere.

Hittil har vi brukt mye copy-paste i våre views. Vi ønsker egentlig å holde ting DRY (Don’t repeat yourself), og i så måte kan vi utnytte Blade i større grad enn vi har gjort hittil.

Lag en ny mappe i views-mappa som heter layouts. Lag en fil som heter app.blade.php. Kopier innholdet fra index.php inn i den. Fjern innholdet i body, og legg til

@yield(‘content’)

Lagre fila, og gå til index.blade.php. Fjern alt utenom innholdet i body’en. På øverste linje arver vi fra layouten som vi nettopp lagde:

@extends(‘layouts.app’)

{{– Så pakker vi innholdet inn i en section: –}}
@section(‘content’)
  <h1>Welcome to Laravel</h1>
  <p>This is a Laravel application</p>
@endsection

Gjør dette med about og services også.

Så kan vi hoppe inn i PagesController for å sette title-tag’en dynamisk

public function index() {
  $title = ‘Welcome to Laravel’;
  return view(‘pages.index’)->with(‘title’, $title);
}

Tilbake i index.blade.php bytter vi ut innholdet i <h1>-tag’en med:

{{ $title }}

Objekt-metoden with() sender altså variabler fra kontrolleren til view’et.

Det anbefales å opprette et assosiativt array, som du deretter sender inn. La oss gjøre det i services-metoden:

public function services() {  $data = array(
    ‘title’ => ‘services’,
    ‘services’ => [‘Web Design’, ‘Programming’, ‘SEO’]
  );

  return view(‘services.index’)->with($data);}

La oss hoppe tilbake inn i services.blade.php, og fjerne <p>-tag’en. Der den var legger vi til:

@if (count($services) > 0)
  <ul>
    @foreach ($services as $service)
      <li>{{ $service }}</li>
    @endforeach
  </ul>
@endif

CSS-filer

Hvis du går til public/css, så finner du app.css. Det er en kompilert fil med css, som inneholder all css’en i prosjektet ditt – inkludert Bootstrap, siden det følger med laravel som standard.

Gå til app.blade.php. I <head>-tag’en legger du til:

<link rel=»stylesheet» href=»{{ asset(‘css/app.css’) }}»>

Når du nå lagrer og laster inn nettleseren på bytt bør du se at vi får en annen bakgrunnsfarge, og en annen skrifttype.

Hvis du foretrekker å jobbe uten SASS kan du enkelt bytte ut alt innholdet i app.css med vanlig css, så fungerer det fint. Hvis du derimot foretrekker SASS må vi gå til rotmappa i terminalen, og kjøre

$ npm install

For å teste at dette var vellykket kan vi gå til resources/assets/sass/_variables.scss, og kommenter ut $body-bg-linja. Lag så en ny linje rett under hvor du setter $body-bg til red, f.eks. Lagre fila, og last inn nettleseren på nytt. Du vil neppe se noen endring, ettersom sass-filene må kompileres først. For å gjøre det kan du gå tilbake til terminalen og kjøre

$ npm run dev

For å slippe å kjøre den hver eneste gang man endrer noe, kan du kjøre denne kommandoen:

$ npm run watch

Som følger med på endringer i filene, og kompilerer direkte.


Hvis du skal legge til annen CSS lager du filer i sass-mappa, med en underscore først i navnet, så f.eks: _custom.scss

Deretter går du til app.scss og lager en ny import-linje helt nederst:

@import «custom»;

Til dette prosjektet bruker vi ganske enkelt Bootstrap, så det blir ikke noe egenskrevet CSS.

Bootstrap

La oss legge til noen bootstrap-klasser for å pynte litt på ting.

Gå til layouts/app.blade.php, og legg en div med container-klassen rundt content:

<div classcontainer«>
  @yield(‘content‘)
</div>

Gå til pages/services.blade.php og legg til klassenavn på ul og li

[…]
  <ul classlistgroup«>
    […]
      <li classlistgroupitem«>{{ $service }}</li>
    […]
  </ul>
[…]

Så vil vi ha en kjapp-og-gæli navigasjonsmeny så vi kan hoppe mellom sidene våre. For ryddighet legger vi det ut i en egen fil som kalles en partial. Lag en mappe i resources/views som du kaller partials. Inni denne lager du en fil som du kaller navbar.blade.php

Dette kan du bare copy-past’e:

<nav class=»navbar navbar-inverse»>
  <div class=»container»>
    <div class=»navbar-header»>
      <button type=»button» class=»navbar-toggle collapsed» data-toggle=»collapse» data-target=»»>
        <span class=»sr-only»>Toggle navigation</span>
        <span class=»icon-bar»></span>
        <span class=»icon-bar»></span>
        <span class=»icon-bar»></span>
      </button>
      <a class=»navbar-brand» href=»/»>{{config(‘app.name’, ‘laravel-crud’)}}</a>
    </div>
    <div id=»navbar» class=»collapse navbar-collapse»>
      <ul class=»nav navbar-nav»>
        <li><a href=»/»>Home</a></li>
        <li><a href=»/about»>About</a></li>
        <li><a href=»/services»>Services</a></li>
      </ul>
    </div>
  </div>
</nav>

Så kan vi slenge inn en jumbotron på forsida. I views/pages/index.blade.php:

  <div class=»jumbotron text-center»>
    <h1>{{ $title }}</h1>
    <p>This is a Laravel application</p>
    <p>
      <a class=»btn btn-primary btn-lg» href=»/login» role=»button»>Login</a>
      <a class=»btn btn-success btn-lg» href=»/register» role=»button»>Register</a>
    </p>
  </div>

Post Model

Vi skal også ha en modell for Posts, som vi kaller Post. Vi kan lage en migrasjon i samme steg, ved å legge til -m som argument:

$ php artisan make:model Post -m

Du vil da finne modell-fila i app-mappa, navngitt Post.php. Hvis du går inn i den ser du at den extender Model-klassen, men har lite innhold forøvrig.

Migrasjonsfila finner du i database/migrations/[timestamp]_create_posts_table.php. Kikk gjerne igjennom for å se hvordan en migrasjonsfil er satt opp. Det er en klasse om extender Migration-klassen. Så har den to metoder, up() og down(). up() skjer når vi kjører migrasjonen, down() skjer når man tar den ned igjen – oftest at man sletter tabellen og tilhørende relasjoner.

La oss legge til noen flere felt. Mellom

$table->increments(‘id’);
$table->timestamp();

Legger du til:

$table->string(‘title’);
$table->mediumText(‘body);

mediumText er lenger enn en streng.

Før vi kjører migrasjonen må vi sette opp en database med en bruker som har tilgang til den.

Når du har brukernavn og passord legger du det inn i .env-fila.

Når det er gjort kan vi prøve å kjøre migrasjonene:

$ php artisan migrate

Hvis alt gikk bra skal du nå ha fire tabeller i databasen din.

Vi kan nå bruke noe som heter “tinker”, for å kommunisere med databasen vår fra kommandolinja.

$ php artisan tinker

Når du har åpnet tinker, kan vi skrive App\modellnavn. Vi kan også kalle metoder, som f.eks:

>>> App\Post::count()

Som i vårt tilfelle returnerer 0, siden vi ikke har noen poster enda.

La oss prøve å lage en post, i Tinker:

>>> $post = new App\Post()

Denne vil kun bli holdt i minne, enn så lenge – den blir altså ikke lagret i databasen. Vi kan fylle objektet med litt data slik:

>>> $post->title = ‘My first post’

Vi kan gjøre det samme med post-body:

>>> $post->body = ‘This is the post body’

Hvis du bare skriver $post, vil du nå kunne se hvordan objektet ser ut:

>>> $post

Hvis du er fornøyd med objektet, og vil lagre det til databasen, kaller du save-metoden:

>>> $post->save()

Lag et objekt til, og lagre det i databasen:

>>> $post = new App\Post()
>>> $post->title = «This is my second post»
>>> $post->body = «This is post number two»
>>> $post->save()

Med det kan vi lukke terminalen.

CRUD

For å legge til CRUD-funksjonalitet i applikasjonen vår trenger vi en del metoder i kontrolleren vår, og en del forskjellige routes i web.php.

PostsController

Vi skal nå lage en artikkel-controller, som vi kaller posts.

$ php artisan make:controller PostsController –resource

–resource gjør at det opprettes en rekke standard crud-funksjoner i controlleren.

Metodene ligger klare, men er tomme – du vil finne:

  • Index: brukes til listing av alle poster
  • Create: brukes for å lage nytt post-objekt
  • Store: brukes for å lagre nytt post-objekt i databasen
  • Show: Viser én enkelt post
  • Edit: Redigerer en post
  • Update: Oppdaterer posten i databasen
  • Destroy: Sletter posten fra databasen


Vi skal gå igjennom disse senere. La oss nå få en oversikt over hvilke endepunkt vi har per nå.

Routing

For å liste ut alle endepunkt:

$ php artisan route:list

Du får da listet opp om det er GET eller POST-endepunkt, hva URL’en er, og hvilken controller som håndterer endepunktet.

Gå nå til routes/web.php. For å unngå å lage en route for hvert endepunkt som tilhører Post-controlleren, kan vi benytte en kortform. Nederst i web.php kan du legge til:

Route::resource(‘posts’, ‘PostsController’);

Hvis du nå lagrer fila, og kjører route:list fra terminalen igjen:

$ php artisan route:list

Så ser du at vi har fått et lass flere endepunkter.

Read – index() og show()

Hvis du går til laravel-crud.test/posts, så får du opp en blank side. Den blanke sida blir levert, fordi vi ikke har fyllt ut index-metoden i PostsController.php med noe nyttig enda. La oss returnere et view:

return view(‘posts.index’);

La oss deretter gå til resources/views, lag en mappe som heter posts, og en fil inni den som heter index.blade.php:

@extends(‘layouts.app’);

@section(‘content’)
  <h1>Posts</h1>
@endsection

Lagre fila, og last inn browseren på nytt. Da ser du innholdet i viewet vi har satt opp. Jippi!

Nå kan vi hoppe tilbake inn i PostsController for å hente ut postene våre. For å gjøre det må vi først importere modellen vår.


Under use Illuminate\Http\Request, legg til:

use App\Post;

Deretter kan vi i en kontroller-metode bruke Post, med en hvilken som helst metode som vi behøver fra modellen (Eloquent-metoder).
I index-metoden, på linja over return view(…), legg til:

return Post::all();

Hvis du lagrer og laster inn browseren på nytt, vil du se at den returnerer alle postene våre som et array med post-objekter.

La oss droppe return, men heller tilegne arrayet til en variabel, og deretter sende det inn til viewet, med with-metoden. Da ender vi opp med:

public function index() {
  $posts = Post::all();
  return view(‘posts.index’)->with(‘posts’, $posts);
}

Tilbake i viewet (resources/views/posts/index.blade.php) kan vi nå løkke igjennom postene. Under h1-tag’en:

@if (count($posts) > 0)
  @foreach($posts as $post)
    <div class=»well»>
      <h3>{{ $post->title }}</h3>
      <small>Written on {{ $post->created_at }}</small>
    </div>
  @endforeach
@else
  <p>No posts found</p>
@endif

Vi kan også legge til link til hver post:

<h3><a href=»{{ route(‘posts.show’, $post->id) }}»>{{ $post->title }}</a></h3>

Hvis du lagrer, laster inn nettleseren på nytt og klikker en av post-titlene, så blir du tatt til en blank side. Den benytter show-metoden i PostsController.php, og mottar id’en til posten. La oss fylle ut show-metoden, slik at den returnerer noe nyttig:

public function show($id) {
  return Post::find($id);
}

Lagre fila, last inn browseren nytt, så vil du se at den finner riktig post, basert på id.

La oss gjøre samme som vi gjorde for index:

public function show($id) {
  $post = Post::find($id);
  return view(‘posts.show’)->with(‘post’, $post);
}

Så vi finner først riktig post, før vi returnerer view’et og sender med post’en.

Fabelaktig!

La oss ta en mellomlanding for å legge til posts i navigasjonen. Gå til views/partials/navbar.blade.php:

<li><a href=»/posts»>Blog</li>

Med det unna vei går vi til resources/views/posts/ og oppretter show.blade.php:

@extends(‘layouts.app’)

@section(‘content’)
  <a href=»{{ route(‘posts.index’) }}» class=»btn btn-default»>Go Back</a>
  <h1>{{ $post->title }}</h1>
  <div>{{ $post->body }}</div>
  <hr>
  <small>Written on {{ $post->created_at }}</small>
@endsection

Lagre, og se hvordan det ble – vi har nå en tilbake-knapp, og vi printer ut litt forskjellig data fra Post-objektet.

Per nå listes postene våre etter hvilken id de har i databasen. Vanligvis er slike poster basert på nyeste dato, så la oss gå tilbake til index-metoden i PostsController.php.

Bytt ut Post::all(); med:

Post::orderby(‘created_at’, ‘desc’)->get();

På all-metoden trenger vi ikke objekt-metoden get, men for alle andre eloquent-spørringer trenger vi den. Hvis du lagrer og henter inn sida på nytt, så ser du at post 2 kommer før post 1.

Vi kan gjøre where-spørringer også, med eloquent, f.eks:

Post::where(‘title’, ‘My first post’)->get();

Du kan også lenke sammen flere metoder, slik som denne:

Post::orderby(‘created_at’, ‘desc’)->take(1)->get();

Take fungerer her som limit, altså at den kun henter én post.

Du kan også bruke ren SQL. Du må først importere database-handleren til Laravel:

use DB;

Og du kan da skrive noe sånt som:

DB::select(‘SELECT * FROM posts’);

Det anbefales kun å bruke der Eloquent ikke strekker til, eller har ytelsesproblemer.

Paginering

Siden vi kun har to poster, legger vi til paginering per én post. Check this shit out:

$posts = Post::orderBy(‘created_at’, ‘desc’)->paginate(1);

I index.blade.php, under foreach’en:

{{ $posts->links() }}

Lagre fila, gå til posts-endepunktet i nettleseren, og vipps har vi paginering!

Create – create() og store()

Vi hopper inn i PostsController, og går til create-metoden. Det vi ønsker her er å returnere et view:

public function create() {
  return view(‘posts.create’);
}

Deretter må vi opprette view’et. Gå til resources/views/posts, og lag en fil som du kaller create.blade.php:

@extends(‘layouts.app’)

@section(‘content’)
  <h1>Create Post</h1>
@endsection

Gå deretter til laravel-crud.test/posts/create, og du skal se viewet vi nettopp lagde. Nå kan vi legge inn en form som tar inn det som trengs for å opprette en ny post. Legg til dette under h1-tag’en:

  <form method=»post» action=»{{ route(‘posts.store’) }}»>
    @csrf
    <div class=»form-group»>
      <label for=»title»>Title</label>
      <input type=»text» class=»form-control» name=»title» placeholder=»Title» />
    </div>

    <div class=»form-group»>
      <label for=»body»>Body</label>
      <textarea class=»form-control» name=»body» cols=»30″ rows=»10″ placeholder=»Body Text»></textarea>
    </div>
    <button type=»submit» class=»btn btn-primary»>Submit</button>
  </form>


Legg merke til at skjemaet sin action er store-metoden som ligger i PostsController.
Vi legger også inn et skjult csrf-felt, som sikrer at skjemaet blir sendt ifra dette nettstedet.

Med skjemaet på plass kan vi fylle ut store-metoden. For å validere felt kan vi gjøre følgende:

public function store(Request $request) {
  $this->validate($request, [
    ‘title’ => ‘required’,
    ‘body’ => ‘required’,
  ]);  return 123;
}

retur-verdien er bare for å sjekke at ting fungerer. Last inn nettleseren på nytt, og prøv å sende inn skjemaet, uten noe innhold i tekstfeltene. Prøv deretter å fylle inn noe, og deretter send skjemaet. Hvis du får tilbake teksten 123, så er på rett vei!

Meldinger

Vi må få inn noe meldingsfunksjonalitet, så vi kan få opp statusmeldinger. La oss lage en ny partial, i resources/views/partials som du kaller messages.blade.php:

@if (count($errors) > 0)
  @foreach ($errors->all() as $error)
  <div classalert alertdanger«>
    {{ $error }}
  </div>
  @endforeach
@endif

$errors er en variabel som vi får tilgang til fra web-middleware, som ligger på alle våre views.

Deretter kan vi sjekke om to session-variabler er satt, henholdsvis success, og error:

@if (session(‘success’))
  <div classalert alertsuccess«>
    {{ session(‘success’)}}
  </div>
@endif

@if (session(‘error’))
  <div classalert alertdanger«>
    {{ session(‘error’)}}
  </div>
@endif

Vi må så legge til denne partial’en i layouts/app.blade.php, inni container-div’en:

<div class=»container»>
  @include(‘partials.messages’)
  @yield(‘content’)
</div>

Lagre fila, og prøv å send inn skjemaet tomt igjen. Fyll deretter ut et av feltene, og send igjen. Fyll ut det siste feltet, og send igjen – og da skal skjemaet slippes igjennom.

La oss gi store-metoden noe mer nyttig funksjonalitet. Vi skal nå opprette et Post-objekt, på samme måte som vi gjorde når vi testet ting i Tinker (kommandolinja). Under valideringen kan du derfor legge til:

$post = new Post;
$post->title = $request->input(‘title’);
$post->body = $request->input(‘body’);
$post->save();

return redirect(route(‘posts.index’))->with(‘success’, ‘Post Created’);

Lagre fila, test at det funker i browseren!

Wysiwyg

For å pynte litt på ting kan vi slenge inn en wysiwyg-provider. I terminalen din:

$ composer require unisharp/laravel-ckeditor

Deretter må du gå til config/app.php. Finn Providers-arrayet, og lim inn dette nederst (ca linje 166)

Unisharp\Ckeditor\ServiceProvider::class,

Deretter må vi kjøre denne kommandoen i terminalen:

php artisan vendor:publish –tag=ckeditor

Vi kan nå gå til views/layouts/app.blade.php. Rett før lukketag’en til body, legg inn dette:

<script src=»/vendor/unisharp/laravel-ckeditor/ckeditor.js»></script>
<script>CKEDITOR.replace(‘article-ckeditor’);</script>

Vi kan nå legge til id’en article-ckeditor til input-tag’en som vi ønsker å bruke som wysiwyg. Så, tilbake i view/posts/create.blade.php, legg til den id’en på textarea-tag’en:

<textarea id=»article-ckeditor» class=»form-control» name=»body» cols=»30″ rows=»10″ placeholder=»Body Text»></textarea>


Og når du nå lagrer og laster inn nettsida på nytt, har vi fått et wysiwyg felt. Dataene vil lagres som HTML, men for at laravel skal tolke HTML’en når vi printer den ut igjen, må vi legge til noe i show.blade.php. Istedenfor {{$post->body}}, må vi bytte ut den innerste krøllparantesen med to utropstegn på hver side:

{!! $post->body !!}

Update – edit() og delete()

Vi kan nå få lagt til en edit-knapp, og en slett knapp, inne i show-viewet. Gå til resources/views/posts/show.blade.php. Under small-tag’en:

<hr>
<a href=»{{ route(‘posts.edit’, $post->id) }}»    class=»btn btn-default»>Edit</a>

Lagre fila.

Gå så til app/Http/Controllers/PostsController.php. Vi skal nå endre edit-metoden:

public function edit($id) {
  $post = Post::find($id);
  return view(‘posts.edit’)->with(‘post’, $post);
}

Denne ligner veldig på show-metoden, så det er ikke så mye å forklare her.

La oss lage et nytt view i posts som vi kaller edit.blade.php. Vi kan kopiere inn skjemaet fra create.blade.php, og gjøre noen små endringer.

På input-feltet må vi sette value til $post->title, og innholdet i textarea-tag’en til $post->body.

Så må vi gjøre noen endringer i form-tag’en. Du kan først sette action:

action=»{{ route(posts.update), $post->id }}»

Vårt endepunkt for update bruker egentlig PUT / PATCH som form method, men i Laravel fungerer det ikke å sette det som method i form-tag’en. Legg derfor til dette rett under csrf_field()

{{ method_field(‘PUT’) }}

Med det gjort kan du lagre fila, og åpne PostsController, for å finne update-metoden.

Du kan begynne med å kopiere innholdet i store-metoden inn i update metoden:

        $this->validate($request, [
            ‘title’ => ‘required’,
            ‘body’ => ‘required’
        ]);

        $post = new Post;
        $post->title = $request->input(‘title’);
        $post->body = $request->input(‘body’);
        $post->save();

        return redirect(route(‘posts.index’))->with(‘success’, ‘Post Created’);

Det eneste vi trenger å endre er linja hvor vi lager en new Post. Vi skal oppdatere en eksisterende post, ikke lage en ny. Bytt linja ut med

$post = Post::find($id);

Du kan også bytte ut success-meldinga med Post Updated, istedenfor Created.

Delete – destroy()

Sletting er ikke en link, men en action, så den må lages på en litt annen måte enn edit-knappen vår. I show.blade.php, under edit-knappen vår må vi derfor legge inn en form:

<form method=»POST» action=»{{ route(‘posts.destroy’, $post->id) }}»>
  {{ csrf_field() }}
  {{ method_field(‘DELETE’) }}
  <div class=»form-group»>
  <input type=»submit» class=»btn btn-danger pull-right» value=»Delete»>
  </div>
</form>

Vi setter altså ruta til posts.destroy, og sender inn id’en på posten.

På samme måte som at laravel ikke støtter PUT som action i en form, støtter de heller ikke DELETE, så vi legger inn method_field på samme måte som tidligere. Ellers er det ganske rett frem. Lagre fila, og la oss hoppe over til PageController.php for å sette opp destroy-metoden:

public function destroy($id) {
    $post = Post::find($id);
    $post->delete();
    return redirect(route(‘posts.index’))->with(‘success’, ‘Post Removed’);
}

Den er jo ganske rett frem: Vi finner posten som blir sendt inn, sletter den, og redirekter tilbake til posts-oversikten

Autentisering

For å legge til brukerautentisering kan vi kjøre en artisan-kommando:

$ php artisan make:auth

Den vil da spørre om den skal overskrive app.blade.php i views/layouts. Kopier ut det vi har der i en midlertidig tekstfil, og la den skrive over fila. 

Klipp ut hele nav-elementet med alt inni fra den nye app.blade.php, og lim den inn i bunn av navbar.blade.php som ligger i views/partials. 

  • Bytt klassen navbar-default til navbar-inverse i den nye nav’en. 
  • Kopier ul-tag’en med innholdet fra den gamle navbar’en vår, slik at vi får med home, about, services og blog. Lim det inn rett under<!– LeftSide of Navbar →
  • Fjern den gamle nav-bar’en fra fila
  • Kopier inn scripts fra den gamle app.blade.php til den nye
  • Bortsett fra script-tag’ene skal body se slik ut:
  <div id=»app»>
    @include(‘partials.navbar’)
    <div class=»container»>
      @include(‘partials.messages’)
      @yield(‘content’)
    </div>
  </div>

Hvis du gjorde alt riktig, så skal du nå fortsatt ha en mørk navbar, med Login og Register til høyre. Disse fungerer helt enkelt ut av boksen, så du kan i teorien begynne å opprette brukere og logge inn. 

Det har blitt opprettet en HomeController.php. Home er litt forvirrende, siden man gjerne forbinder home med forsiden / hjemmesiden. Home i denne sammenhengen er egentlig dashboardet til hver bruker. Så la oss endre navn på den til DashboardController. 

HomeController.php: endre navn på fila, og klassenavnet i fila. Endre deretter index-metoden til å returnere view’et ‘dashboard’, istedenfor ‘home’. Lagre fila, og gå til Controllers/Auth/LoginController.php, og endre $redirectTo til ‘dashboard’. Gjør det samme i RegisterController.php og til slutt det samme i ResetPasswordController.php.

Så endrer vi routes/web.php. Nederst skal det ha blitt opprettet en ny rute, endre den til:

Route::get(‘/dashboard’, ‘DashboardController@index’);

Kun ett sted gjenstår – endre resources/views/home.blade.php til – du gjettet riktig, dashboard.

Med alt lagret og klart kan du lage en bruker. Logg ut igjen, og logg inn igjen for å sjekke at det fungerer.

Vi kan nå gå til dashboard.blade.php, og legge inn litt. Fjern teksten “You are logged in!”, og i dets sted, legg til

<a href=»{{ route(‘posts.create’) }}» class=»btn btn-primary»>Create Post</a>

Når vi nå går til dashboardet, vil vi se en Create Post link, som tar oss til Create Post-viewet.

Relasjoner


Det neste vi ønsker er å koble en post mot brukeren som har skrevet den. Da må vi først lage en migrasjon som oppretter en ny kolonne i tabellen posts, som tar vare på bruker ID’en.

For å gjøre det lager vi en migrasjon i terminalen:

$ php artisan make:migration add_user_id_to_posts

Som forventet blir det opprettet en ny migrasjonsfil. Den er forholdsvis tom, så vi må fylle inn i  up() og down-metodene:

public function up() {
  Schema::table(‘posts’, function($table) {
    $table->integer(‘user_id’);
  });
}

Vi velger altså hvilken tabell vi skal gjøre noe med, og sender inn en funksjon som gjør noe. I vårt tilfelle, legger til et integer-felt som vi kaller user_id. Kopier hele innholdet til down-metoden. Den eneste forskjellen er at vi bytter $table->integer(‘user_id’); med:

$table-dropColumn(‘user_id’);

Kjør deretter migrasjonen

$ php artisan migrate


Dermed har vi det feltet. La oss så gå til PostsController.php. I store-metoden må vi legge til user_id per post, slik at vi kan definere hvilken bruker som eier en post.

Legg til

$post->user_id = auth()->user()->id;

Vi får da inn altså hvilken bruker som er logget inn. 

Siden vi først er i PostsController, endre redirect-url’en i store-metoden til dashboard:

return redirect(‘/dashboard’)->with(‘success’, ‘Post Created’);

Vi skal teste at dette fungerer, men først legger vi til en link til Dashboard i navbar.blade.php. Finn ul tag’en med klasse dropdown-menu, og legg til denne li’en øverst i den:

<li><a href=»{{ route(‘dashboard’) }}»>Dashboard</a></li>

Lagre, gå til nettleseren og refresh. Hvis ikke du er logget inn, logg deg inn. Du skal bli sendt til dashboardet, hvis ikke ligger nå lenken i dropdown-menyen som ligger oppe til høyre. Trykk create, og lag en post. Hvis den er laget og alt gikk fint, så kan du gå tilbake til terminalen og åpne tinker:

$ php artisan tinker

Så, i tinker kan vi sjekke den siste posten ved å kjøre

>>> Post::all()->last()

Og hvis alt er på stell fikk du ut riktig post, med user_id’en lik din user-id. Jippi!

Foreløbig er ikke Post og User koblet sammen, men det tar vi hånd om nå. Gå først til Post.php, og lag en funksjon der som heter user:

public function user(){
  return $this->belongsTo(‘App\User’);
}

Det vi sier her er altså at en Post har en relasjon til en User. Den tilhører – belongsTo en bruker.

Vi definerer gjerne også den andre siden av relasjonen, altså fra User til Post. Gå til User.php:

public function posts() {
  return $this->hasMany(‘App\Post’);
}

Så en bruker har mange poster. Legg merke til at metodenavnet er posts, altså flertall.

Disse to metodene vi nå har lagt til gjør at vi kan bruke et lass med metoder på modellene våre som ikke var tilgjengelig tidligere. Lagre filene og gå til DashboardController.php:

public function index() {
  $user_id = auth()->user()->id;
  $user = User::find($user_id);
  return view(‘dashboard’)->with(‘posts’, $user->posts);
}

Det som er tøft med dette er with-argumentet, altså at vi kan hente en brukers poster ved å benytte $user->posts. Neat!

I samme fil, rett under use Illuminate\Http\Request, legg til:

use App\User;

Dette gjør vi for å få tak i bruker-objektet i dashboardet.

Gå nå til dashboard.blade.php i views-mappa. Under create-knappen legg til følgende:

@if (count($posts) > 0)
  <table class=»table table-striped»>
    <tr>
      <th>Title</th>
      <th></th>
      <th></th>
  </tr>
    @foreach ($posts as $post)
      <tr>
        <td>{{ $post->title }}</td>
        <td><a href=»{{ route(‘posts.edit’, $post->id) }}»                class=»btn btn-default»>Edit</a></td>
        <td>Delete</td>
    </tr>
    @endforeach

  </table>
@else
  <p>You have no posts</p>
@endif

Hvis du nå logger ut, lager en ny bruker og logger inn, bør du ha ingen poster. Prøv deretter å lag noen poster, og gå til dashboardet for å se at de ligger der – kun de som tilhører den nye brukeren. Fabelaktig! Edit-knappen fungerer, men la oss legge til funksjonalitet for Delete-knappen også. Det kan vi kopiere rett fra posts.show, så bare kopier det inn i den tredje td’en i foreach løkka:

<form method=»POST» action=»{{ route(‘posts.destroy’, $post->id) }}»>
  {{ csrf_field() }}
  {{ method_field(‘DELETE’) }}
  <div class=»form-group»>
    <input type=»submit» class=»btn btn-danger»
    value=»Delete»>
  </div>
</form>

La oss nå også legge til forfatternavnet på alle postene som ligger i posts.index. I small-tag’en legg til

by {{ $post->user[‘name’] }}

Gå til show.blade.php og legg inn det samme i small-tag’en der.

Autorisering

For å tvinge brukeren til innloggingsskjermen når de ikke er logget inn, må vi legge inn en middleware i controller’en. Den legger vi i konstruktøren. Hvis ikke du har en konstruktør lager du den bare. Gå til PostsController.php:

public function __construct() {
  $this->middleware(‘auth’);
}

Dette vil tvinge innlogging på alle controller-metodene. Vi ønsker at man skal kunne se oversikten over poster, og en enkelt post. For å gjøre det legger vi til et argument til i middleware’en:

$this->middleware(‘auth’, [‘except’ => [‘index’, ‘show’]]);

Vi sender altså inn et array med en opsjon, except, og sender inn et array over kontroller-metodene som skal ignoreres. Neat!

Per nå har vi edit og delete knapper på hver enkelt post, men de gjør ingenting. La oss skjule dem for alle andre enn den som eier posten. Gå til views/posts/show.blade.php. Pakk inn form-group’en nederst med

@if(Auth::user() == $post->user)
  <div class=»form-group»>
    …
  </div>
@endif


Det neste punktet er at hvis man går til posts/1/edit, så vil alle se det viewet og det ønsker vi ikke. Gå til PostsController og finn edit-metoden. Under der vi finner post’en:

if (auth()->user()->id !== $post->user_id) {
  return redirect(‘/posts’)->with(‘error’, ‘Unauthorized Page’);
}

Nøyaktig den samme sjekken kan legges til i destroy og update-metodene i samme kontroller, for å unngå brukere kan slette poster laget av andre brukere.