Skip to content

Instantly share code, notes, and snippets.

@yoeunes
Forked from cjthomp/50 Laravel Tricks.md
Created January 7, 2018 14:20
Show Gist options
  • Save yoeunes/a3005f3c7c1f099265bbd916eacef6be to your computer and use it in GitHub Desktop.
Save yoeunes/a3005f3c7c1f099265bbd916eacef6be to your computer and use it in GitHub Desktop.
50 Laravel Tricks

Reference

  1. https://speakerdeck.com/willroth/50-laravel-tricks-in-50-minutes
  2. https://www.reddit.com/r/laravel/comments/3to60i/50_laravel_tricks/

ToC

A. Eloquent
  • 1. Automatic Model Validation
  • 2. Prevent Uploading
  • 3. Conditional Relationships
  • 4. Expressive "where" syntax
  • 5. Query Builder: having raw
  • 6. Simple Date filtering
  • 7. Save options
  • 8. Multilanguage Support *
  • 9. Retreive random rows
  • 10. UUID Model Primary Keys
  • 11. Ordered relationships
  • 12. Simple incrementing and Decrementing
  • 13. Lists with Mutations
  • 14. Appending mutated properties
  • 15. Filter only rows with Child rows
  • 16. Return relations on model save
B. Blade
  • 17. Dynamic With
  • 18. First/Last Array Element
C. Collections
  • 19. Arrays as Collections
  • 20. Collection Filters
  • 21. find()
  • 22. where()
  • 23. implode()
  • 24. where() & list()
  • 25. Order belongsToMany by pivot table value
  • 26. Sorting with closures
  • 27. Keying arrays
  • 28. Grouped Collections
  • 29. Collection Unions
  • 30. Collection Look-aheads
D. Routing
  • 31. Nested Route groups
  • 32. Catch-all view route
  • 33. Internal dispatch
E. Testing
  • 34. Environment Variables
  • 35. Run tests automatically
F. Miscellaneous
  • 36. Share cookies between domains
  • 37. Easy model and migration stubs
  • 38. Add Spark to an existing project
  • 39. Customize the default error page
  • 40. Conditional service providers
  • 41. Change a column name in a migration
  • 42. Checking if a view exists
  • 43. Extending the Application
  • 44. Simple cache microservice
  • 45. Using the bleeding-edge version of Laravel
  • 46. Capture queries
  • 47. Authorization without models
  • 48. Efficient file transfers with Streams
  • 49. Avoiding overflowing log files
  • 50. Pipelines

ELOQUENT

1. Automatic Model Validation

class Post extends Eloquent
{
	public staic $autoValidates = true;

	protected static $rules = [];

	protected static function boot()
	{
		parent::boot();

		// or static::creating, or static::updating
		static::saving(function($model)
		{
			if ($model::$autoValidates) {
				return $model->validate();
			}
		});
	}

	public function validate()
	{

	}
}

2. Prevent updating

class Post extends Eloquent
{
	protected static function boot()
	{
		parent::boot();

		static::updating(function($model)
		{
			return false;
		});
	}
}

3. Conditional Relationships

class myModel extends Model
{
	public function category()
	{
		return $this->belongsTo('myCategoryModel', 'categories_id')
			->where('users_id', Auth::user()->id);
	}
}

4. Expressive "Where" Syntax

$products = Product::where('category', '=', 3)->get();

$products = Product::where('category', 3)->get();

$products = Product::whereCategory(3)->get();

5. Query Builder: Having Raw

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();

6. Simple Date Filtering

$q->whereDate('created_at', date('Y-m-d'));

$q->whereDay('created_at', date('d'));

$q->whereMonth('created_at', date('m'));

$q->whereYear('created_at', date('Y'));

7. Save Options

// src/Illuminate/Database/Eloquent/Model.php
public function save(array $options = [])

// 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 00:00:00';
$product->save(['timestamps'=>false]);

8. Multilanguage Support

// TODO

9. Retrieve Random Rows

$questions = Question::orderByRaw('RAND()')->take(10)->get();

10. UUID Model Primary Key

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->generateNewUuid();
			}
		});
	}

	public function generateNewUuid()
	{
		return Uuid::uuid4();
	}
}

11. Ordered Relationships

class Category extends Model
{
	public function products()
	{
		return $this->hasMany('App\Product')->orderBy('name');
	}
}

12. Simple Incrementing & Decrementing

$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);

13. Lists with Mutations

$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'...Column not found:...'full_name'

$employees = Employee::where('branch_id', 9)->get()->lists('full_name', 'id');

14. Appending Mutated Properties

function getFullNameAttribute() 
{
	return $this->first_name . ' ' . $this->last_name;
}

class User extends Model
{
	protected $appends = ['full_name'];
}

15. Filter only rows with child rows

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'));
}

16. Return relations on model save

public function store()
{
	$post = new Post;
	$post->fill(Input::all());
	$post->user_id = Auth::user()->user_id;

	$post->user;

	return $post->save();
}

Blade

17. Dynamic With

// eloquent
Post::whereSlug('slug')->get();

// instead of
View::make('posts.index')->with('posts', $posts);

// do this
View::make('posts.index')->withPosts($posts);

18. First/Last Array Element

// 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

Collections

19. Arrays as Collections

$devs = [
	['name' => 'Anouar Abdessalam', 'email' => '[email protected]'],
	['name' => 'Bilal Ararou', 'email' => '[email protected]'],
];

$devs = new \Illuminate\Support\Collection($devs);

20. Collection Filters

Keeps the item only if the closure returns true

$customers = Customer::all();

$us_customers = $customers->filter(function($customer)
{
	return $customer->country == 'United States';
});

21. find()

// returns a single row as a collection
$collection = Person::find([1]);

// returns multiple rows as a collection
$collection = Person::find([1, 2, 3]);

22. where()

$collection = Person::all();

$programmers = $collection->where('type', 'programmer');

23. implode()

$collection = Person::all();

$names = $collection->implode('first_name', ',');

24. where() & list()

// returns a collection of first names
$collection = Person::all()->where('type', 'engineer')->lists('first_name');

// returns all meta records for user 1
$collection = WP_Meta::whereUserId(1)->get();

// returns first name meta values
$first_name = $collection->where('meta_key', 'first_name')->lists('value')[0];

25. Order belongsToMany by Pivot Table value

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)
		...
	@endforeach
@endif

26. Sorting with closures

$sorted = $collection->sortBy(function($product, $key)
{
	return array_search($product['name'], [1=>'Bookcase', 2=>'Desk', 3=>'Chair']);
});

27. Keying arrays

Defines the 'key' for an array-as-collection (for use with e.g. ->contains)

$library = $books->keyBy('title');

28. Grouped Collections

$collection = Person::all();

$grouped = $collection->groupBy('type');

29. Collection Unions

// the point is to actually combine results from different models
$collection = new Collection;

$all = $collection->merge($programmers)->merge($critics)->merge($engineers);

30. Collection Lookaheads

$collection = collect([1=>11, 5=>13, 12=>14, 21=>15])->getCachingIterator();

foreach ($collection as $key=>$value)
{
	dump ($collection->current() . ':' . $collection->getInnerIterator()->current());
}

Routing

31. Nested Route Groups

Route::group(['prefix'=> => 'account', 'as' => 'account.'], function()
{
	Route::get('login', ['as' => 'login', 'uses' => AccountController::Class.'@getLogin']);
});

<a href="{{ route('account.login') }}">Login</a>

32. Catch-all View Route

// 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', '.*');
});

33. Internal Dispatch

// api controller
public funciton 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);

Testing

34. Environmental Variables

// phpunit.xml
<php
	<env name="APP_ENV" value="testing" />
</php>

// .env.test - add to .gitignore
TWILIO_ACCOUNT_SID=blank

// within createApplication() method of TestCase.php
if (file_exists(dirname(__DIR__) . '/.env.test')) {
	Dotenv::load(dirname(__DIR__), '.env.test');
}

35. Run tests automatically

// gulpfile.js
var elixir = require('laravel-elixir');

mix.phpUnit();

$ gulp tdd

Miscellaneous

36. Share Cookies Between Domains

// app/Http/Middleware/EncryptCookies.php
protected $except = [
	'shared_cookie',
];

Cookie::queue('shared_cookie', 'my_shared_value', 10080, null, '.example.com');

37. Easy Model & Migration Stubs

$ artisan make:model Books -m

38. Add Spark to an Existing Project

Notes: Do not run spark:install, backup /resources/views/home.blade.php before running

$ composer require genealabs/laravel-sparkinstaller --dev

$ php artisan spark:upgrade

$ php artisan vendor:publish --tag=spark-full
// config/app.php
Laravel\Spark\Providers\SparkServiceProvider::class,
GeneaLabs\LaravelSparkInstaller\Providers\LaravelSparkInstallerServiceProvider::class,

39. Customize the Default Error Page

<?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);
		
		return $debug
			? (new SymfonyDisplayer($debug))->createResponse($e)
			: response()->view('errors.default', ['exception' => $e], 500);
	}
}

40. Conditional Service Providers

// app/Providers/AppServiceProvider.php
public function register()
{
	$this->app->bind(
		\Illuminate\Contracts\Auth\Registrar::class,
		\App\Services\Registrar::class
	);

	if ($this->app->environment('production')) {
		$this->app->register(\App\Providers\ProductionErrorHandlerServiceProvider::class);
	}
	else {
		$this->app->register(\App\Providers\VerboseErrorHandlerServiceProvider::class);
	}
}

41. Change a Column Name in Migration

$ composer require doctrine/dbal

public function up()
{
	Schema::table('users', function ($table)
	{
		$table->string('name', 50)->change();
	});
}

42. Checking if a View Exists

if (view()->exists("emails.{$template}")) {
	// ...sending an email to the customer
}

43. Extending the Application

// bootstrap/app.php
// replace this:
$app = new \Illuminate\Foundation\Application( realpath(__DIR__.'/../'));

// with this:
$app = new \Fantabulous\Application( realpath(__DIR__.'/../'));

// and add
<?php namespace Fantabulous;

class Application extends \Illuminate\Foundation\Application
{
	public function storagePath()
	{
		return $this->basePath.'/FantabulousStorage';
	}
}

44. Simple Caching Microservice

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', '.*');

45. Use Bleeding Edge Version

$ composer create-project laravel/laravel your-project-name dev-develop

// composer.json
{
	"require": {
		"php": ">=5.5.9",
		"laravel/framework": "5.2.*"
	},
	"minimum-stability": "dev"
}

46. Capture Queries

Event::listen('illuminate.query', function($query)
{
	var_dump($query);
});

\DB::listen(function($query, $bindings, $time)
{
	var_dump( $query, $bindings, $time);

});

47. Authorization Without Models

// 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::class . "@{$method}");
	}
	$this->registerPolicies($gate);
}

$this->authorize('managePages'); // in Controllers
@can('managePages') // in Blade
$user->can('managePages'); // via Eloquent

48. Efficient File Transfers with Streams

$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);

49. Avoid Overflowing Log Files

$schedule->call(function()
{
	Storage::delete($logfile);
})->weekly();

50. Pipeline

$result = (new \Illuminate\Pipeline\Pipeline($container))
	->send($something)
	->through('ClassOne', 'ClassTwo', 'ClassThree')
	->then(function ($something)
	{
		return 'foo';
	});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment