Laravel 5.3 & future plans

Sooo L5.3 will release in a few days and finally have some time to think. My last year was a little crowded with projects but have some big plans.

  • YouTube tutorials
  • Live brainstorm & problem solving
  • Vue.js 2.0
  • Zurb Foundation 6.2+
  • Implement my old laravel pakages for 5.3
  • Server (linux, maybe bsd too) tutorials
  • Gaming stuff perhaps [GTX650 -> RX480 upgrade]
  • and more

If you read this please help me what kind of content would like to see 🙂

YES the content diversity will increase also my non existing video editing skill.

I make some demo profile in twitter, youtube and twitch and create content there too however I’m not a socialmedia guy if you want it will do it!!

Fun fact Gmail cut out the dots from the username/email address (foo.bar = foobar), twitter not allowed to use but underscore is okay…

Advertisements

Mutator update for Laravel News feed (SEO)

This is a little update for my Laravel News feed extend with SEO friendly URL and jquery post.

I want to change the news/item/link.blade.php from

<article>
   <a href="{{ route('news.read', $url ) }}">{{ $headline }}</a>
   <p>{{ $story }}</p>
</article>

To:

<article>
   <a href="{{ $url }}">{{ $headline }}</a>
   <p>{{ $story }}</p>
</article>

Because this is pretty !!!!


To do this need to add laravel magic function (Mutator) to News model, when you try to get the url field / attribute change the return value to full url. In the url need the key value.
I try few code all goes to infinite loop 😦
The dynamic result is:

    public function getUrlAttribute() {
        return route( 'news.read' , [$this->attributes[$this->getKeyName()]] );
    }

Short the controller too 🙂

class NewsController extends Controller {
    public function first() {
        Session::put( 'news.page' , 0 );
        return view( 'news.first' )->withNews( News::getMore()->toArray() );
    }

    public function more() {
        if ( Session::has( 'news.page' ) ) {
            $page = Session::get( 'news.page' ) + 1;
            Session::put( 'news.page' , $page );

            return view( 'news.list.more' )->withNews( News::getMore( $page )->toArray() );
        }
    }

    public function read( News $news ) {
        return view( 'news.read' )->with( $news->toArray() );
    }
}

Laravel 5 Authentication, Filter – Request – Controller

This is the fourth part of my Authentication tutorial. The glue!! Where everything start working 🙂

In laravel 5 have a file generator command for Authentication :

> php artisan make:auth

Controller created successfully.
Route: $router->controller('auth', 'App\Http\Controllers\Auth\AuthController');

Controller created successfully.
Route: $router->controller('password', 'App\Http\Controllers\Auth\RemindersController');

Authentication requests created successfully.

Migration created successfully.
Generating optimized class loader
Compiling common classes
Compiling views

This make you a few almost ready to use files:

Filter:
laravel/app/Http/Filters/AuthFilter.php

Requests:
laravel/app/Http/Requests/Auth/LoginRequest.php
laravel/app/Http/Requests/Auth/RegisterRequest.php

Controllers:
laravel/app/Http/Controllers/Auth/AuthController.php
laravel/app/Http/Controllers/Auth/RemindersController.php

Migration:
laravel/database/migrations/2014_09_29_134856_create_password_reminders_table.php

for now I skip the password reminder

Auth Filter

Basically this is done from the start. I add my login route and comment out the else part.

namespace App\Http\Filters;

use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Contracts\Auth\Authenticator;
use Illuminate\Contracts\Routing\ResponseFactory;

class AuthFilter {
    protected $auth;
    protected $response;

    public function __construct( Authenticator $auth , ResponseFactory $response ) {
        $this->auth = $auth;
        $this->response = $response;
    }

    public function filter( Route $route , Request $request ) {
        if ( $this->auth->guest() ) {
            if ( $request->ajax() ) {
                return $this->response->make( 'Unauthorized' , 401 );
            }
            //else {
            //    return $this->response->redirectGuest( 'auth/login' );
            //}
            return $this->response->redirectGuest( route( 'login' ) );
        }
    }
}

Form Requests

The Register Request is DONE 🙂
If you use different table for users change the unique rule’s table name.

// RegisterRequest
namespace App\Http\Requests\Auth;

use Illuminate\Foundation\Http\FormRequest;

class RegisterRequest extends FormRequest {
    public function rules() {
        return [
            'name' => 'required' ,
            'email' => 'required|email|unique:users' ,
            'password' => 'required|confirmed|min:8' ,
        ];
    }

    public function authorize() {
        return true;
    }
}

The Login Request also DONE.
I add two function what can use the controllers later:
credentials to get the credentials (and easy to change them here) 🙂
remember to determine the remember me checkbox on/off status

// LoginRequest 
namespace App\Http\Requests\Auth;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest {
    public function rules() {
        return [
            'email' => 'required' ,
            'password' => 'required' ,
        ];
    }

    public function remember() {
        return $this->has( 'remember' );
    }

    public function credentials() {
        return $this->only( 'email' , 'password' );
    }

    public function authorize() {
        return true;
    }
}

Auth Controller

So the generated controller almost done, some function in skeleton state so need to add your logic.
I add my named routes here too, and use intended redirection.
And user Register use my User model.

So the first thing is import the model and Redirect facade:

namespace App\Http\Controllers\Auth;

use Illuminate\Routing\Controller;
use Illuminate\Contracts\Auth\Authenticator;

use App\Http\Requests\Auth\LoginRequest;
use App\Http\Requests\Auth\RegisterRequest;

use Illuminate\Support\Facades\Redirect;
use App\User;

In the constructor all post Request add csrf filter, and guest filter everywhere except the logout.

class AuthController extends Controller {

    protected $auth;

    public function __construct( Authenticator $auth ) {
        $this->auth = $auth;

        $this->beforeFilter( 'csrf' , ['on' => ['post']] );
        $this->beforeFilter( 'guest' , ['except' => ['getLogout']] );
    }

In get methods set your views. In logout Redirect home named route.

    public function getLogin() {
        return view( 'auth.login' );
    }

    public function getRegister() {
        return view( 'auth.register' );
    }

    public function getLogout() {
        $this->auth->logout();

        //return redirect( '/' );
        return Redirect::route( 'home' );
    }

In the postRegister need to add a line where create the user and login it.
If you prefer to use email validation – activation link stuff just comment the login line out.
note: you can add some kind of getRegisterUserData function to RegisterRequest and call it for user create

    public function postRegister( RegisterRequest $request ) {
        $user = User::create( $request->all() ); // new line
        $this->auth->login( $user );

        //return redirect( '/' );
        return Redirect::route( 'home' );
    }

In postLogin:
Use credentials and remember from LoginRequest to login attempt.
Get user data.
Redirect to the intended page or fallback to the default route if no intended page.

    public function postLogin( LoginRequest $request ) {
        if ( $this->auth->attempt( $request->credentials() , $request->remember() ) ) {

            $user = $this->auth->user();
            // if you use database not eloquent driver 
            //$user = User::where( 'id' , '=' , $this->auth->user()->id )->first();
            
            //return redirect( '/' );
            return Redirect::intended( route( 'profile' , [$user->getKey()] ) );
        }

        //return redirect( 'auth/login' )->withErrors( [
        return Redirect::route( 'login' )->withErrors( [
                    'email' => 'The credentials you entered did not match our records. Try again?' ,
                ] );
    }

And thats all.
After login you go the profile page, or the intended page. You can register a user too 🙂

Laravel 5 Authentication, User Model

This is the third part of my Authentication tutorial.

The basic Laravel 5 install contains a User model, now poke this a bit.
Add soft delete, and default password hash, set the fillable fields, change the primary key if you like:

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model implements AuthenticatableContract, CanResetPasswordContract {

	use Authenticatable, CanResetPassword, SoftDeletes;

	protected $table = 'users';
	protected $fillable = ['name', 'email', 'password'];
	protected $hidden = ['password', 'remember_token'];

       // public $incrementing = false;
       // protected $primaryKey = 'name';

        protected $dates = ['deleted_at'];

        public function setPasswordAttribute($password) {
           $this->attributes['password'] = bcrypt($password);
       }
}

Laravel 5 Authentication, Users Migration & Seed

This is the second part of my Authentication tutorial. Just a little Migration and optional Seeding 🙂

Migration

Create a migration:

> php artisan make:migration --create users create_users_table
  Created Migration: 2014_09_23_121027_create_users_table

Add your fields, I set email to unique because for Authentication use this. If you like can use username.
And add two good Laravel feature: soft delete, remember token 🙂

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateUsersTable extends Migration {
 
    public function up() {
        Schema::create( 'users' , function(Blueprint $table) {
            $table->increments( 'id' );
            $table->rememberToken();
            $table->timestamps();
            $table->softDeletes();
            $table->string( 'email' )->unique();
            $table->string( 'password' , 64 );
            $table->string( 'name' );
        } );
    }
 
    public function down() {
        Schema::drop( 'users' );
    }
}

Add migration:

> php artisan migrate
Migrated: 2014_09_23_121027_create_users_table

The Seeding not necessary. I write the register part too, for testing good 🙂
Create a UserTableSeeder file in laravel/database/seeds folder:
note: if you already add an attribute modifier in User model use raw input data in seeding

use Illuminate\Database\Seeder;
use App\User as User;
 
class UserTableSeeder extends Seeder {
 
    public function run() {
        User::truncate();
 
        User::create( [
            'email' => 'sheepy85@test.com' ,
            'password' => Hash::make( 'password' ) ,
            'name' => 'sheepy85' ,
        ] );
    }
}

Generating autoload and seed:

> php artisan optimize
> php artisan db:seed

Laravel 5 Authentication, Route & View

In this part need some protected page (Profile), a public login and a register page.

But first add few link to the base layout menu.

Let see need a little logic:
– show the login/register menu only for Guests
– show profile and logout only for Users
and I put the user’s name also 🙂

Just add the new menu items, and float right the login/logout.
As you will see I use Laravel’s Auth Class to get current user data or check it is a guest.
sure use named routes and generate profile url by add the second parameter as the user’s primary key:

route('profile', Auth::user()->getKey() )

or an array parameter see replaceRoutableParameters in Illuminate\Routing\UrlGenerator

Why getKey and not id? Good question thank you for ask 🙂

Imagine a scenario where you use the username as identification and unique field. In this case every code where expecting username will fail because use make url by Auth::user()->id and not username.

By use models getKey always get the current primary key value, also don’t need rewrite your views 🙂

// User model
protected $primaryKey = 'id'  // default
// url: my.app/profile/1

// or
protected $primaryKey = 'username'
// url: my.app/profile/foo_user

The code:

<!-- view/layouts/base.blade.php -->
@if(Auth::guest())
   <li class="pull-right">
      <a href="{{ route('login') }}">login</a>
      / <a href="{{ route('register') }}">register</a>
   </li>
@else
   <li><a href="{{ route('profile', Auth::user()->getKey() ) }}">Profile</a></li>
   <li class="pull-right">{{ Auth::user()->name }}<a href="{{ route('logout') }}">logout</a></li>
@endif

Profile view. Nothing extra print the name and email.

<!-- view/profile/index.blade.php -->
@extends('layouts.base')
  
@section('body')
<section class="col-md-9">
    <h1>Profile: {{ $name }}</h1>
    <p>email: {{ $email }}</p>
</section>
@stop

Login view. I not use HTML package (not part of the default Laravel 5 installation)

Put some input, email, password and remember option 🙂

<!-- view/auth/login.blade.php -->
@extends('layouts.base')
  
@section('body')
<section class="col-md-9">
   <h1>Login</h1>
   <form action="{{ route('post.login') }}" method="post" accept-charset="utf-8" role="form">
      <input type="hidden" name="_token" value="{{ csrf_token() }}">
  
      <!-- print errors -->
      @foreach($errors->all() as $error)
      <p>{{$error}}</p>
      @endforeach
  
         <label for="email">Email:</label>
         <input type="email" name="email" id="email" value="{{ old('email') }}" placeholder="your@email.com">
  
         <label for="password">Password</label>
         <input type="password" name="password" id="password" value="{{ old('password') }}">
  
         <label>
            <input type="checkbox" name="remember" id="remember" value="1" {{ old('remember') ? 'checked' : '' }}> Remember me
         </label>
  
         <button type="submit">Login</button>
   </form>
</section>
@stop

Register almost the same, without remember and a plus one check for the password (min 6 character) and your name

<!-- view/auth/login.blade.php -->
@extends('layouts.base')

@section('body')
<section class="col-md-9">
    <h1>register</h1>
    <form action="{{ route('post.register') }}" method="post" accept-charset="utf-8" role="form">
        <input type="hidden" name="_token" value="{{ csrf_token() }}">

        <!-- print errors -->
        @foreach($errors->all() as $error)
        <p>{{$error}}</p>
        @endforeach
        
         <label for="email">Email:</label>
         <input type="email" name="email" id="email" value="{{ old('email') }}" placeholder="your@email.com">
         
         <label for="name">Name:</label>
         <input type="text" name="name" id="name" value="{{ old('name') }}" placeholder="your name">
  

        <label for="password">Password (min 6)</label>
        <input type="password" name="password" id="password" value="{{ old('password') }}">

        <label for="password_confirmation">Password again</label>
        <input type="password" name="password_confirmation" id="password_confirmation" value="{{ old('password_confirmation') }}">

        <button type="submit">send</button>
    </form>
</section>
@stop

Routes are tricky.

First set the user model because the profile will use it as parameter.

Route::model( 'user' , 'App\User' );

Set the protected page list with auth middleware filter:
ProfileController see below

Route::group( [
    'middleware' => 'auth' ,
        ] , function() {
  
    get( '/profile/{user}' , [
        'as' => 'profile' ,
        'uses' => 'ProfileController@index'
    ] );
} );

Default Authentication routes:
You get this when use artisan make:auth command (Auth\AuthController), I also add the route names.

Route::controllers([
	'auth' => 'Auth\AuthController',
	'password' => 'Auth\PasswordController',
]);

If you don’t want this default url perfix (my.app/auth/login) need to define individually the routes:


get( '/register' , [
    'as' => 'register' ,
    'uses' => 'Auth\AuthController@getRegister'
] );
  
post( '/register' , [
    'as' => 'post.register' ,
    'uses' => 'Auth\AuthController@postRegister'
] );
  
get( '/login' , [
    'as' => 'login' ,
    'uses' => 'Auth\AuthController@getLogin'
] );
  
post( '/login' , [
    'as' => 'post.login' ,
    'uses' => 'Auth\AuthController@postLogin'
] );
  
get( '/logout' , [
    'as' => 'logout' ,
    'uses' => 'Auth\AuthController@getLogout'
] );

Because I change the route for the login page from auth/login need to add the new route to AuthController:

class AuthController extends Controller {
   private $loginPath = '/login';
...

and change the Middleware secure login here:

// App\Http\Middleware\Authenticate
class Authenticate {
...
   public function handle($request, Closure $next)
...
      else {
         return redirect()->guest('/login');
      }
...

ProfileController:

// > php artisan make:controller --plain ProfileController

namespace App\Http\Controllers;
use App\User;

class ProfileController extends Controller {

    public function index(User $user) {
        return view('profile.index')->with($user->toArray());
    }

}

Laravel 5 Authentication, part zero

In Laravel 5 have a new approach for Auth, input Request and a few artisan command to speed up the process.
So the last days I try to make an authentication filter for my example page.

No code in this post just write my comments.

Create a basic authentication is not too hard tons of tutorials can find in the net however the first version what I write is based on old Laravel 4 style. After few hour delete all and start over 🙂

My problems are the named route and different user identification fields.

In this point Laravel 5 still in development and Illuminate\Auth\GenericUser not have the same features as normal App\User for example can’t use different ID field then id when you chose database driver in auth settings…

Keep in mind this.

I will use getKey method to get the identification value, and hope implement this in GenericUser too 🙂

Part 1: make the entry points, because these are fix things 🙂
– the Route and View stuff.

Part 2: will need some user data so make database tables and seed them.
– Users Migrations & Seed

Part 3: this is short one but important if you use different primary key.
– User Model

Part 4: Create and extend AuthController
– Authentication Controller

Laravel News feed extend with SEO friendly URL and jquery

Now I extend my Pagination News feed and replace the Pagination with show more link and the default id to url 🙂

SEO friendly URL

I replace the current id based url to SEO friendly url:

// current
http://laravel.dev/news/read/1
// seo
http://laravel.dev/news/read/news-url

How can do it in Laravel?

First need to modify the News Model to use different primary key.
I add a new method to query limited records.

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingTrait;

class News extends Model {

    use SoftDeletingTrait;

    protected $dates = ['deleted_at'];
    protected $fillable = ['headline' , 'story'];
    protected $perPage = 1; // news per page

    public $incrementing = false;
    protected $primaryKey = 'url';
    protected $hidden = ['id' , 'deleted_at']; // hide from print

    public function scopegetMore( $query , $page = 0 ) {
        return $query->skip( $page * $this->perPage )->take( $this->perPage )->get();
    }
}

Because now use url as key need to update the item.link blade $id to $url:

<article>
   <a href="{{ route('news.read', $url ) }}">{{ $headline }}</a>
   <p>{{ $story }}</p>
</article>

Well we are done, now use database stored url for identify the records 🙂


Show More News

The basic idea is ‘send and receive data in the background’.
Need a new route, controller method and a view.

The route:

get( '/news/more' , [
    'as' => 'news.more' ,
    'uses' => 'NewsController@more'
] );

The Method:
You need to store somewhere the page number what the user last get. I use Session variable but can use GET / POST data too.
The Session way clear and less messy 🙂

Logic:
The first page set the Session variable and return the news blade layout with the last news.
The more page increment the Session variable, store it and return one page news (add to previous ones)
If Session variable not exit return nothing

...
use Illuminate\Support\Facades\Session;

class NewsController extends Controller {

    public function first() {
        Session::put( 'news.page' , 0 );
        $news = News::getMore()->toArray();
        return view( 'news.first' , compact( 'news' ) );
    }

    public function more() {
        if ( Session::has( 'news.page' ) ) {
            $page = Session::get( 'news.page' ) + 1;
            Session::put( 'news.page' , $page );

            $news = News::getMore( $page )->toArray();
            return view( 'news.list.more' , compact( 'news' ) );
        }
    }
...

The View:
news/list/more.blase.php
nothing extra, put the same news items as before

@if(count($news))
    @foreach($news as $item)
        @include('news.item.link', $item)
    @endforeach
@endif

The button and jquery:
This is where the magic happening 🙂

Here replace the Pagination with button / link and jquery.
I just put a link and pop-in the data, no animation.. yet 🙂

The first part is the first method result almost the same as before 🙂
and the link with an id ‘moreNews’
finally the jquery.

Jquery use the same url as the link.
Hide the link if data not returned (no more news).
Insert data before the link.

news/list/link.blade.php

@if(count($news))
    @foreach($news as $item)
        @include('news.item.link', $item) 
    @endforeach

<a id="moreNews" href="{{ route( 'news.more' ) }}">Show More</a>

<script>
    $( '#moreNews' ).click( function (event) {
       // default action of the event will not be triggered
       event.preventDefault();

       $btn = $( this );

       $.ajax( {
          url : $btn.attr( 'href' ),
          dataType : 'html',
          timeout : 10000
       } ).done( function (data) {
          if ( !data ) { // no data hide link
             $btn.hide();
             return;
          }
          $btn.before( data ); // add data
       } ).fail( function (jqXHR, textStatus, error) {
          if ( textStatus == 'timeout' ) {
             alert( 'Error, please try again later! (' + textStatus + ')' );
          }
          else { // error|abort|parsererror
             alert( 'Error, please try again later! (' + textStatus + ')' );
          }
       } );
    } );
</script>

@else
    @include('news.empty')
@endif

Done.

Basic Laravel Pagination News feed

In My previous post created a Migration and Seed the database with some basic data, now just print this data.
And the Next post show you a more fun way to do it with json and jquery 🙂

Planning

For the better usage and think to the future need new controller to handle the News logic (get, post, edit).
Need new route ways something news and news/read.
Have a News model need to add some settings here too.
And the Views.


Route

Set two basic route with name (/news, /news/read/{id}) where the id is our News model class 🙂

Route::model('news', 'App\News');
 
get( '/news' , [
    'as' => 'news' ,
    'uses' => 'NewsController@first'
] );
 
get( '/news/read/{news}' , [
    'as' => 'news.read' ,
    'uses' => 'NewsController@read'
] );

Views

In my layouts/base.blade.php just add the new menu item:

<li><a href="{{ route('news') }}">News</a></li>

Next thing is the News list it self. I split this 5 logical unit:

  1. extends the layouts.base for the list
  2. empty list
  3. news list links
  4. news item with link
  5. extends the layouts.base for read news item

make the files and put them folders:

resources/
   views/
      news/
        empty.blade.php
        first.blade.php
        read.blade.php
        list/
          link.blade.php
        item/
          link.blade.php

first will be my starting point where the list loads first
list.link make a list of item.link(s)

first.blade.php

@extends('layouts.base')
 
@section('body')
<section class="col-md-9">
    <h1>News</h1>
    <p>Soo Hot</p>
     
    @include('news.list.link')
</section> 
@stop

empty.blade.php

@extends('layouts.base')
 
@section('body')
<section class="col-md-9">
    <h1>News</h1>
    <p>Sorry, no news posted yet.</p>
</section> 
@stop

read.blade.php

@extends('layouts.base')
 
@section('body')
<section class="col-md-9">
   <h1>{{ $headline }}</h1>
   {{ $story }}
</section> 
@stop

item/link.blade.php

<article>
   <a href="{{ route('news.read', [ $id ] ) }}">{{ $headline }}</a>
   <p>{{ $story }}</p>
</article>

list/link.blade.php
This one is tricky, you can use @each method too but take care ‘news.item.link’ variables and check the count to for Pagination links.

@if(count($news))
    @foreach($news as $item)
        @include('news.item.link', $item) 
    @endforeach

   {!! $news->links() !!}
@else
    @include('news.empty')
@endif

News Model

A little pimp it for soft delete and limit the item per page for Pagination nothing extra:

// App/News.php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingTrait;

class News extends Model {
    use SoftDeletingTrait;

    protected $dates = ['deleted_at'];
    protected $fillable = ['headline' , 'story'];
    protected $perPage = 10;
}

News Controller

Make a new blank controller

> php artisan make:controller NewsController --plain

And implement the two method you set in route:

namespace App\Http\Controllers;

use App\News;

class NewsController extends Controller {

    public function first() {
        // set Pagination feature with default value (News model $perPage)
        $news = News::paginate();
        return view( 'news.first' , compact( 'news' ) );
    }

    public function read( News $news ) {
        // read the news(id)
        return view( 'news.read' )->with( $news->toArray() );
    }
}

Well this is all 🙂
Fire up in controller, use in view.

In my opinion : cool feature but paginate() creates really big object so try to avoid this or make your own.

In next, extend this with a load / show more button at the end of news list, and get more news via jquery.