Skip to content

Instantly share code, notes, and snippets.

@cjthomp
Last active April 5, 2024 05:50
Show Gist options
  • Save cjthomp/1455c39d4a14292676ea to your computer and use it in GitHub Desktop.
Save cjthomp/1455c39d4a14292676ea 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' => 'dtekind@gmail.com'],
	['name' => 'Bilal Ararou', 'email' => 'have@noIdea.com'],
];

$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';
	});
@amouillard
Copy link

amouillard commented Nov 8, 2016

Hey,

18. First/Last Array Element

Inside of @ loops you can now access the $loop variable which has 'first' and 'last' attributes

// hide all but the first item
@foreach ($menu as $item)
    <div @if ($loop->first) class="hidden" @endif>
        <h2>{{ $item->title }}</h2>
    </div>
@endforeach

// apply CSS to last item only
@foreach ($menu as $item)
    <div @if ($loop->last) class="no_margin" @endif>
        <h2>{{ $item->title }}</h2>
    </div>
@endforeach

19. Arrays as Collections

You can now use the collect() helper

$devs = [
    ['name' => 'Anouar Abdessalam', 'email' => 'dtekind@gmail.com'],
    ['name' => 'Bilal Ararou', 'email' => 'have@noIdea.com'],
];

$devs = collect($devs);

@sergiy-petrov-vakoms
Copy link

in "1. Automatic Model Validation" there is typo:

"public staic $autoValidates = true;" in static word

@aozen
Copy link

aozen commented May 29, 2019

25. Order belongsToMany by Pivot Table value
Using take(15) increases your chances of getting error. limit(15) is safer than take(15). If collection doesn't have 15 value, the error occurs.
People may think your usage was wrong, but the problem may be in the take() function.

@if ($link->users->count() > 0)
	<strong>Recently Favorited By</strong>
	@foreach ($link->users()->orderBy('link_user.created_at', 'desc')->limit(15)->get() as $user)
		...
	@endforeach
@endif

@abdegenius
Copy link

sorting from a collection is just the best trick ever

@deviddev
Copy link

  1. Retrieve Random Rows
$questions = Question::orderByRaw('RAND()')->take(10)->get();

instead

$questions = Question::inRandomOrder()->take(10)->get();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment