Upgrade to Pro — share decks privately, control downloads, hide ads and more …

50 Laravel Tricks in 50 Minutes

willroth
November 19, 2015

50 Laravel Tricks in 50 Minutes

Laravel 5.1 raised the bar for framework documentation, but there's much, much more lurking beneath the surface. In this 50-minute session, we'll explore 50 (yes, 50!) high-leverage implementation tips & tricks that you just won't find in the docs: the IoC Container, Blade, Eloquent, Middleware, Routing, Commands, Queues, Events, Caching — we'll cover them all! Join us as we drink from the fire hose & learn to take advantage of everything that Laravel has to offer to build better software faster!
---
Presented on 11/19/2015 at PHP World

willroth

November 19, 2015
Tweet

More Decks by willroth

Other Decks in Technology

Transcript

  1. class Post extends Eloquent { public static $autoValidate = true;

    protected static $rules = array(); protected static function boot() { parent::boot(); // You can also replace this with static::creating or static::updating static::saving(function($model) { if ($model::$autoValidate) { return $model->validate(); } }); } public function validate() { } }
  2. class Post extends Eloquent { protected static function boot() {

    parent::boot(); static::updating(function($model) { return false; }); } }
  3. class myModel extents Model { public function category() { return

    $this->belongsTo('myCategoryModel', 'categories_id') ->where('users_id', Auth::user()->id); } }
  4. SELECT *, COUNT(*) FROM products GROUP BY category_id HAVING count(*)

    > 1; DB::table('products') ->select('*', DB::raw('COUNT(*) as products_count')) ->groupBy('category_id') ->having('products_count', '>' , 1) ->get(); Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
  5. // src/Illuminate/Database/Eloquent/Model.php public function save(array $options = array()) // src/Illuminate/Database/Eloquent/Model.php

    protected function performUpdate(Builder $query, array $options = []) { if ($this->timestamps && array_get($options, 'timestamps', true)) { $this->updateTimestamps(); } $product = Product::find($id); $product->updated_at = '2015-01-01 10:00:00'; $product->save(['timestamps' => false]);
  6. // app/Article.php class Article extends Model { use \Dimsav\Translatable\Translatable; public

    $translatedAttributes = ['name', 'text']; } // database/migrations/create_articles_table.php public function up() { Schema::create('articles', function (Blueprint $table) { $table->increments('id'); $table->boolean('online'); $table->timestamps(); }); } // resources/views/article.blade.php <h1>{{ $article->name }}</h1> {{ $article->text }} //database/migrations/create_articles_table.php public function up() { $table->increments('id'); $table->integer('article_id')->unsigned(); $table->string('locale')->index(); $table->string('name'); $table->text('text'); $table->unique(['article_id','locale']); $table->foreign('article_id')->references('id') ->on('articles') ->onDelete('cascade'); } // app/ArticleTranslation.php class ArticleTranslation extends Model { public $timestamps = false; } // app/http/routes.php Route::get('{locale}', function($locale) { app()->setLocale($locale); $article = Article::first(); return view('article')->with(compact('article')); }); http://50LaravelTricksIn50Minutes.com/fr -- French Translation
  7. use Ramsey\Uuid\Uuid; trait UUIDModel { public $incrementing = false; protected

    static function boot() { parent::boot(); static::creating(function ($model) { $key = $model->getKeyName(); if (empty($model->{$key})) { $model->{$key} = (string) $model->generateNewId(); } }); } public function generateNewUuid() { return Uuid::uuid4(); } }
  8. class Category extends Model { public function products() { return

    $this->hasMany('App\Product')->orderBy(‘name'); } }
  9. $customer = Customer::find($customer_id); $loyalty_points = $customer->loyalty_points + 50; $customer->update(['loyalty_points' =>

    $loyalty_points]); // adds one loyalty point Customer::find($customer_id)->increment('loyalty_points', 50); // subtracts one loyalty point Customer::find($customer_id)->decrement('loyalty_points', 50);
  10. $employees = Employee::where('branch_id', 9)->lists('name', 'id'); return view('customers.create', compact('employees')); {!! Form::select('employee_id',

    $employees, '') !!} public function getFullNameAttribute() { return $this->name . ' ' . $this->surname; } [2015-07-19 21:47:19] local.ERROR: exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'full_name' in 'field list'' in ...vendor\laravel\framework\src\Illuminate\Database\Connection.php:288 $employees = Employee::where('branch_id', 9)->get()->lists('full_name', 'id');
  11. function getFullNameAttribute() { return $this->first_name . ' ' . $this->last_name;

    } { "id":1, "first_name":"Povilas", "last_name":"Korop", "email":"[email protected]", "created_at":"2015-06-19 08:16:58", "updated_at":"2015-06-19 19:48:09" } class User extends Model { protected $appends = ['full_name']; { "id":1, "first_name":"Povilas", "last_name":"Korop", "email":"[email protected]", "created_at":"2015-06-19 08:16:58", "updated_at":"2015-06-19 19:48:09", "full_name":"Povilas Korop" }
  12. class Category extends Model { public function products() { return

    $this->hasMany('App\Product'); } } public function getIndex() { $categories = Category::with('products')->has('products')->get(); return view('categories.index', compact('categories')); }
  13. public function store() { $post = new Post; $post->fill(Input::all()); $post->user_id

    = Auth::user()->user_id; $post->user; return $post->save() }
  14. //hide all but the first item @foreach ($menu as $item)

    <div @if ($item != reset($menu)) class="hidden" @endif> <h2>{{ $item->title }}</h2> </div> @endforeach //apply css to last item only @foreach ($menu as $item) <div @if ($item == end($menu)) class="no_margin" @endif> <h2>{{ $item->title }}</h2> </div> @endforeach
  15. $devs = [ ['name' => 'Anouar Abdessalam','email' => '[email protected]'], ['name'

    => 'Bilal Ararou','email' => '[email protected]'] ]; $devs = new Illuminate\Support\Collection($devs); $devs->first(); $devs->last(); $devs->push(['name' => 'xroot','email' => '[email protected]']);
  16. $customers = Customer::all(); $us_customers = $customers->filter(function ($customer) { return $customer->country

    == 'United States'; }); $non_uk_customers = $customers->reject(function ($customer) { return $customer->country == 'United Kingdom'; });
  17. // returns a single row as a collection $collection =

    App\Person::find([1]); // can return multiple rows as a collection $collection = App\Person::find([1, 2, 3]);
  18. // returns a collection of first names $collection = App\Person::all()->where('type',

    'engineer')->lists('first_name'); // returns all the meta records for user 1 $collection = App\WP_Meta::whereUserId(1)->get(); // returns the first & last name meta values $first_name = $collection->where('meta_key', 'first_name')->lists('value')[0]; $last_name = $collection->where('meta_key', 'last_name')->lists('value')[0];
  19. class Link extends Model { public function users() { return

    $this->belongsToMany('Phpleaks\User')->withTimestamps(); } } @if ($link->users->count() > 0) <strong>Recently Favorited By</strong> @foreach ($link->users()->orderBy('link_user.created_at', 'desc')->take(15)->get() as $user) <p> <a href="{{ URL::Route('user.show', array('id' => $user->id)) }}">{{ $user->name }}</a> </p> @endforeach @endif
  20. $collection = collect([ ['name' => 'Desk'], ['name' => 'Chair'], ['name'

    => 'Bookcase'] ]); $sorted = $collection->sortBy(function ($product, $key) { return array_search($product['name'], [1=>'Bookcase', 2=>'Desk', 3=>'Chair']); });
  21. $library = $books->keyBy('title'); [ 'Lean Startup' => ['title' => 'Lean

    Startup', 'price' => 10], 'The One Thing' => ['title' => 'The One Thing', 'price' => 15], 'Laravel: Code Bright' => ['title' => 'Laravel: Code Bright', 'price' => 20], 'The 4-Hour Work Week' => ['title' => 'The 4-Hour Work Week', 'price' => 5], ]
  22. // the point is to actually combine results from different

    models $programmers = \App\Person::where('type', 'programmer')->get(); $critic = \App\Person::where('type', 'critic')->get(); $engineer = \App\Person::where('type', 'engineer')->get(); $collection = new Collection; $all = $collection->merge($programmers)->merge($critic)->merge($engineer);
  23. $collection = collect([1=>11, 5=>13, 12=>14, 21=>15])->getCachingIterator(); foreach ($collection as $key

    => $value) { dump($collection->current() . ':' . $collection->getInnerIterator()->current()); }
  24. Route::group(['prefix' => 'account', 'as' => 'account.'], function () { Route::get('login',

    ['as' => 'login', 'uses' => 'AccountController@getLogin']); Route::get('register', ['as' => 'register', 'uses' => 'AccountController@getRegister']); Route::group(['middleware' => 'auth'], function () { Route::get('edit', ['as' => 'edit', 'uses' => 'AccountController@getEdit']); }); }); <a href="{{ route('account.login') }}">Login</a> <a href="{{ route('account.register') }}">Register</a> <a href="{{ route('account.edit') }}">Edit Account</a>
  25. // app/Http/routes.php Route::group(['middleware' => 'auth'], function () { Route::get('{view}', function

    ($view) { try { return view($view); } catch (\Exception $e) { abort(404); } })->where('view', '.*'); });
  26. // api controller public function show(Car $car) { if (Input::has('fields'))

    { // do something } } // internal request to api - fields are lost $request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET'); $response = json_decode(Route::dispatch($request)->getContent()); // internal request to api - with fields $originalInput = Request::input(); $request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET'); Request::replace($request->input()); $response = json_decode(Route::dispatch($request)->getContent()); Request::replace($originalInput);
  27. // phpunit.xml <php> <env name="APP_ENV" value="testing"/> <env name="CACHE_DRIVER" value="array"/> <env

    name="SESSION_DRIVER" value="array"/> <env name="QUEUE_DRIVER" value="sync"/> <env name="DB_DATABASE" value=":memory:"/> <env name="DB_CONNECTION" value="sqlite"/> <env name="TWILIO_FROM_NUMBER" value="+15005550006"/> </php> // .env.test – add to .gitignore TWILIO_ACCOUNT_SID=fillmein TWILIO_ACCOUNT_TOKEN=fillmein // tests/TestCase.php <?php class TestCase extends Illuminate\Foundation\Testing\TestCase { /** * The base URL to use while testing the application. * * @var string */ protected $baseUrl = 'http://localhost'; /** * Creates the application. * * @return \Illuminate\Foundation\Application */ public function createApplication() { $app = require __DIR__.'/../bootstrap/app.php'; if (file_exists(dirname(__DIR__) . '/.env.test')) { Dotenv::load(dirname(__DIR__), '.env.test'); } $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); return $app; } } // access directly from your tests using helper function env('TWILIO_ACCOUNT_TOKEN');
  28. $ composer require genealabs/laravel-sparkinstaller --dev // Laravel\Spark\Providers\SparkServiceProvider::class, GeneaLabs\LaravelSparkInstaller\Providers\LaravelSparkInstallerServiceProvider::class, // do

    not run php artisan spark:install $ php artisan spark:upgrade // backup /resources/views/home.blade.php or it will be overwritten $ php artisan vendor:publish --tag=spark-full
  29. <?php namespace App\Exceptions; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use

    Symfony\Component\Debug\ExceptionHandler as SymfonyDisplayer; class Handler extends ExceptionHandler { protected function convertExceptionToResponse(Exception $e) { $debug = config('app.debug', false); if ($debug) { return (new SymfonyDisplayer($debug))->createResponse($e); } return response()->view('errors.default', ['exception' => $e], 500); } }
  30. // app/Providers/AppServiceProvider.php public function register() { $this->app->bind( 'Illuminate\Contracts\Auth\Registrar', 'App\Services\Registrar' );

    if ($this->app->environment('production')) { $this->app->register('App\Providers\ProductionErrorHandlerServiceProvider'); } else { $this->app->register('App\Providers\VerboseErrorHandlerServiceProvider'); } }
  31. // bootstrap/app.php // replace this: $app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../')

    ); // with this: $app = new Fantabulous\Application( realpath(__DIR__.'/../') ); <?php namespace Fantabulous; class Application extends \Illuminate\Foundation\Application { /** * Get the path to the storage directory. * * @return string */ public function storagePath() { return $this->basePath.'/FantabulousStorage'; } }
  32. class fakeApiCaller { public function getResultsForPath($path) { return [ 'status'

    => 200, 'body' => json_encode([ 'title' => "Results for path [$path]" ]), 'headers' => [ "Content-Type" => "application/json" ] ]; } } $app->get('{path?}', function($path) { $result = Cache::remember($path, 60, function() use ($path) { return (new fakeApiCaller)->getResultsForPath($path); }); return response($result['body'], $result['status'], array_only( $result['headers'], ['Content-Type', 'X-Pagination'] )); })->where('path', '.*');
  33. $ composer create-project laravel/laravel your-project-name-here dev-develop // composer.json { "require":

    { "php": ">=5.5.9", "laravel/framework": "5.2.*" }, "minimum-stability": "dev" } $ composer update
  34. // app/Policies/AdminPolicy.php class AdminPolicy { public function managePages($user) { return

    $user->hasRole(['Administrator', 'Content Editor']); } } // app/Providers/AuthServiceProvider.php public function boot(\Illuminate\Contracts\Auth\Access\GateContract $gate) { foreach (get_class_methods(new \App\Policies\AdminPolicy) as $method) { $gate->define($method, "App\Policies\AdminPolicy@{$method}"); } $this->registerPolicies($gate); } $this->authorize('managePages'); // in Controllers @can('managePages') // in Blade Templates $user->can('managePages'); // via Eloquent
  35. $disk= Storage::disk('s3'); $disk->put($targetFile, file_get_contents($sourceFile)); $disk = Storage::disk('s3'); $disk->put($targetFile, fopen($sourceFile, 'r+'));

    $disk = Storage::disk('s3'); $stream = $disk->getDriver()->readStream($sourceFileOnS3); file_put_contents($targetFile, stream_get_contents($stream), FILE_APPEND); $stream = Storage::disk('s3')->getDriver()->readStream($sourceFile); Storage::disk('sftp')->put($targetFile, $stream)
  36. class PurchasePodcastCommand extends Command { public $user; public $podcast; public

    function __construct(User $user, Podcast $podcast) { $this->user = $user; $this->podcast = $podcast; } } class PurchasePodcastCommandHandler { public function handle(BillingGateway $billing) { // Handle the logic to purchase the podcast... event(new PodcastWasPurchased($this->user, $this->podcast)); } } class PodcastController extends Controller { public function purchasePodcastCommand($podcastId) { $this->dispatch( new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId)) ); } }
  37. class PurchasePodcast extends Command implements SelfHandling { protected $user; protected

    $podcast; public function __construct(User $user, Podcast $podcast) { $this->user = $user; $this->podcast = $podcast; } public function handle(BillingGateway $billing) { // Handle the logic to purchase the podcast... event(new PodcastWasPurchased($this->user, $this->podcast)); } } class PodcastController extends Controller { public function purchasePodcast($podcastId) { $this->dispatch( new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId)) ); } }
  38. class PodcastController extends Controller { public function purchasePodcast(PurchasePodcastRequest $request) {

    $this->dispatchFrom('Fantabulous\Commands\PurchasePodcastCommand', $request); } } class PodcastController extends Controller { public function purchasePodcast(PurchasePodcastRequest $request) { $this->dispatchFrom('Fantabulous\Commands\PurchasePodcastCommand', $request, [ 'firstName' => 'Taylor', ]); } }
  39. class PurchasePodcast extends Command implements ShouldBeQueued, SerializesModels { public $user;

    public $podcast; public function __construct(User $user, Podcast $podcast) { $this->user = $user; $this->podcast = $podcast; } }
  40. // App\Providers\BusServiceProvider::boot $dispatcher->pipeThrough(['UseDatabaseTransactions', 'LogCommand']); class UseDatabaseTransactions { public function handle($command,

    $next) { return DB::transaction(function() use ($command, $next) { return $next($command); }); } } // App\Providers\BusServiceProvider::boot $dispatcher->pipeThrough([function($command, $next) { return DB::transaction(function() use ($command, $next) { return $next($command); }); }]);
  41. <p> <input type="text" name="person[1][id]"> <input type="text" name="person[1][name]"> </p> <p> <input

    type="text" name="person[2][id]"> <input type="text" name="person[2][name]"> </p> $v = Validator::make($request->all(), [ 'person.*.id' => 'exists:users.id', 'person.*.name' => 'required:string', ]);