├── docs ├── 0075-authorization.md ├── 0005-database.md ├── 0051-auth-component.md ├── 0039-one-to-many.md ├── 0024-deleting.md ├── 0004-controllers.md ├── 0002-routes-views.md ├── 0026-assets.md ├── 0011-views-collections-dates.md ├── 0009-models-eloquent.md ├── 0010-resource-controllers.md ├── 0006-migrations.md ├── 0028-testing.md ├── 0012-form-saving.md └── 0036-one-to-one.md ├── resources └── img │ ├── login-flow.png │ ├── middleware.png │ ├── one-to-one.png │ ├── one-to-many.png │ ├── Laravel-Routes.png │ ├── auth-components.png │ ├── 0005-create-database.gif │ ├── one-to-many-database.png │ ├── one-to-one-database.png │ ├── Laravel-Route-HTTP-Verbs.png │ ├── Laravel-Resource-Controller.png │ └── 0005-create-database.screenflow │ ├── thumbnail.jpg │ ├── version.plist │ ├── ScreenFlowDocument.dat │ └── Media │ └── C71468AF-9693-4F73-BC79-91A159D228FF_{8140FCA5-41D3-49E0-BF75-A8FC36CCF4AB}.scc └── README.md /docs/0075-authorization.md: -------------------------------------------------------------------------------- 1 | ## Authorization 2 | 3 | ### Gates 4 | 5 | 6 | 7 | ### Policies 8 | 9 | ### Authorizing -------------------------------------------------------------------------------- /resources/img/login-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/login-flow.png -------------------------------------------------------------------------------- /resources/img/middleware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/middleware.png -------------------------------------------------------------------------------- /resources/img/one-to-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/one-to-one.png -------------------------------------------------------------------------------- /resources/img/one-to-many.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/one-to-many.png -------------------------------------------------------------------------------- /resources/img/Laravel-Routes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/Laravel-Routes.png -------------------------------------------------------------------------------- /resources/img/auth-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/auth-components.png -------------------------------------------------------------------------------- /resources/img/0005-create-database.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/0005-create-database.gif -------------------------------------------------------------------------------- /resources/img/one-to-many-database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/one-to-many-database.png -------------------------------------------------------------------------------- /resources/img/one-to-one-database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/one-to-one-database.png -------------------------------------------------------------------------------- /resources/img/Laravel-Route-HTTP-Verbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/Laravel-Route-HTTP-Verbs.png -------------------------------------------------------------------------------- /resources/img/Laravel-Resource-Controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/Laravel-Resource-Controller.png -------------------------------------------------------------------------------- /resources/img/0005-create-database.screenflow/thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/0005-create-database.screenflow/thumbnail.jpg -------------------------------------------------------------------------------- /resources/img/0005-create-database.screenflow/version.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/0005-create-database.screenflow/version.plist -------------------------------------------------------------------------------- /resources/img/0005-create-database.screenflow/ScreenFlowDocument.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/0005-create-database.screenflow/ScreenFlowDocument.dat -------------------------------------------------------------------------------- /resources/img/0005-create-database.screenflow/Media/C71468AF-9693-4F73-BC79-91A159D228FF_{8140FCA5-41D3-49E0-BF75-A8FC36CCF4AB}.scc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piotr-jura-udemy/laravel-cheat-sheet/HEAD/resources/img/0005-create-database.screenflow/Media/C71468AF-9693-4F73-BC79-91A159D228FF_{8140FCA5-41D3-49E0-BF75-A8FC36CCF4AB}.scc -------------------------------------------------------------------------------- /docs/0005-database.md: -------------------------------------------------------------------------------- 1 | ### Configuring database 2 | 3 | The database connection parameters are stored inside .env file (on development environment). 4 | 5 | On production environment store it in VirtualHost configuration or .htaccess file. To store variables in .htaccess file VirtualHost must contain the `AllowOverride All` directive. 6 | 7 | Reading environment variable 8 | 9 | ``` 10 | $value = env('DB_HOST', '127.0.0.1'); 11 | ``` 12 | 13 | *Where first value is the name of the variable, and second is the default value (if env variable is not defined).* 14 | 15 | ### Connecting to the database server 16 | 17 | #### Using phpMyAdmin 18 | 19 | Visit [http://localhost/phpmyadmin](http://localhost/phpmyadmin) if you are using XAMPP 20 | 21 | Create a new database 22 | 23 | ![Creating database in phpMyAdmin](./../resources/0005-create-database.gif) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Cheat Sheet 2 | 3 | ## Laravel basics (routing, controllers, templates/views, forms, testing, assets) 4 | 5 | 1. [Routes, Views and Layout](./docs/0002-routes-views.md) 6 | 2. [Controllers](./docs/0004-controllers.md) 7 | 3. [Configuring Database](./docs/0005-database.md) 8 | 4. [Database Migrations](./docs/0006-migrations.md) 9 | 5. [Eloquent Models and Tinker](./docs/0009-models-eloquent.md) 10 | 6. [Resource Controllers](./docs/0010-resource-controllers.md) 11 | 7. [Blade @foreach, @if and Dates (Carbon)](./docs/0011-views-collections-dates.md) 12 | 8. [Forms, CSRF, Request, Middleware, Redirects](./docs/0012-form-saving.md) 13 | 9. [Deleting Models, Soft Deletes](./docs/0024-deleting.md) 14 | 10. [Assets, JavaScript, Styles](./docs/0026-assets.md) 15 | 11. [Testing basics](./docs/0028-testing.md) 16 | 17 | ## Database Relations 18 | 19 | 1. [One to One relations](./docs/0036-one-to-one.md) 20 | 2. [One to Many relations](./docs/0039-one-to-many.md) 21 | 22 | ## Authentication 23 | 24 | 1. [Authentication components](./docs/0051-auth-component.md) 25 | -------------------------------------------------------------------------------- /docs/0051-auth-component.md: -------------------------------------------------------------------------------- 1 | ## Authentication 2 | 3 | ### Authentication component 4 | 5 | Below is a diagram of Laravel Authentication component: 6 | 7 | ![Authentication components](./../resources/img/auth-components.png) 8 | 9 | The main component responsible for authenticating users is called **Guard**. 10 | 11 | **Guard** has 2 components: 12 | 13 | * Driver (how to handle user authenticated state - keep in session/read from token) 14 | * User Provider (where to find user accounts) 15 | 16 | **User Provider** is responsible for fetching users from certain storage (usually a database). 17 | 18 | **User Provider** also has a **Driver**, it can be: 19 | 20 | * Database (fetching users using raw SQL query) 21 | * Eloquent (use User model to fetch the user) 22 | 23 | Laravel has some sensible defaults configured out of the box. There are 2 guards: 24 | 25 | * **web** (for traditional web applications, using session to store currently authenticated user) 26 | * **api** (for APIs, stateless - no session is kept between request, each request needs to include a valid token to authenticate user) 27 | 28 | The default Laravel guard is **web**. 29 | 30 | ### Default login flow 31 | 32 | Below is the diagram of how logging in works by default: 33 | 34 | ![Login flow](./../resources/img/login-flow.png) -------------------------------------------------------------------------------- /docs/0039-one-to-many.md: -------------------------------------------------------------------------------- 1 | ## One-to-Many relations 2 | 3 | An example one-to-many relation diagram: 4 | 5 | ![Graph](./../resources/img/one-to-many.png) 6 | 7 | ## Database design 8 | 9 | ![](./../resources/img/one-to-many-database.png) 10 | 11 | With one-to-many relation, 1 record from first table (eg. `blog_posts`) has zero or more matching records on the other table (eg. `comments`). 12 | 13 | One of the tables needs to have a column that will hold the `id` of the record on the other table. 14 | 15 | In our example, we have `blog_posts` and `comments`. `BlogPost` can be related to 1, 2, 3...n `Comment`, so the relation has to be stored inside `Comment`. 16 | 17 | In short: 18 | 19 | * has* - methods on Models that own the relation, no relation column 20 | * belongs* - methods on Models that are other side of the relation, with relation column 21 | 22 | In the example above each `BlogPost` model has zero or more `Comment`. 23 | 24 | ## Defining relations 25 | 26 | The relation on `BlogPost` model: 27 | 28 | ``` 29 | class BlogPost extends Model 30 | { 31 | public function comments() 32 | { 33 | return $this->hasMany('App\Comment'); 34 | } 35 | } 36 | ``` 37 | 38 | Relation on `Comment` model: 39 | 40 | ``` 41 | class Comment extends Model 42 | { 43 | public function blogPost() 44 | { 45 | return $this->belongsTo('App\BlogPost'); 46 | } 47 | } 48 | ``` 49 | 50 | ## Migrations 51 | 52 | `BlogPost` already exists and requires no changes. 53 | 54 | `Comment` model migration: 55 | 56 | ``` 57 | Schema::create('comments', function (Blueprint $table) { 58 | $table->increments('id'); 59 | $table->timestamps(); 60 | $table->string('content'); 61 | 62 | $table->unsignedInteger('blog_post_id')->index(); 63 | $table->foreign('blog_post_id')->references('id')->on('blog_posts'); 64 | }); 65 | ``` -------------------------------------------------------------------------------- /docs/0024-deleting.md: -------------------------------------------------------------------------------- 1 | ### Deleting models 2 | 3 | To delete a model, call the `delete` method on model instance 4 | 5 | ``` 6 | $post = BlogPost::findOrFail($id); 7 | $post->delete(); 8 | ``` 9 | 10 | Alternatively, without loading the model first 11 | 12 | ``` 13 | BlogPost::destroy($id); 14 | ``` 15 | 16 | Or to delete a collection of models at once 17 | 18 | ``` 19 | BlogPost::delete([1, 2, 3]); 20 | ``` 21 | 22 | #### Soft deleting 23 | 24 | You may optionally opt to keep the original record in database, and just mark it with `deleted_at` field. 25 | 26 | To enable it easily, use the `SoftDeletes` trait on the model 27 | 28 | ``` 29 | namespace App; 30 | 31 | use Illuminate\Database\Eloquent\Model; 32 | use Illuminate\Database\Eloquent\SoftDeletes; 33 | 34 | class BlogPost extends Model 35 | { 36 | use SoftDeletes; 37 | 38 | protected $dates = ['deleted_at']; 39 | } 40 | ``` 41 | 42 | You need to remember to add the `deleted_at` column to the Model 43 | 44 | ``` 45 | Schema::table('blog_posts', function (Blueprint $table) { 46 | $table->softDeletes(); 47 | }); 48 | ``` 49 | 50 | To determine if a particular model instance was deleted use `trashed` method. 51 | 52 | ``` 53 | $isDeleted = $post->trashed(); 54 | ``` 55 | 56 | Now when you query for models, the soft deleted models are automatically exlcuded from results. 57 | 58 | ``` 59 | // This will never include models that were "soft deleted" 60 | $posts = BlogPost::all(); 61 | ``` 62 | 63 | To include also the "soft deleted" models 64 | 65 | ``` 66 | BlogPost::withTrashed()->get(); 67 | ``` 68 | 69 | To fetch only trashed models 70 | 71 | ``` 72 | BlogPost::onlyTrashed()->get(); 73 | ``` 74 | 75 | To restore a soft deleted model 76 | 77 | ``` 78 | $post->restore(); 79 | ``` 80 | 81 | If a model is "soft deleteable" and you wish to permanently remove it 82 | 83 | ``` 84 | $post->forceDelete(); 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/0004-controllers.md: -------------------------------------------------------------------------------- 1 | ### Controllers 2 | 3 | Controllers are an alternative to Closures for defining the application logic 4 | 5 | Generating a new Controller 6 | 7 | `php artisan make:controller ControllerNameController` 8 | 9 | Controllers are stored inside the `app/Http/Controllers` folder 10 | 11 | An example controller class 12 | 13 | ```php 14 | 'value']); // With parameters 22 | ``` 23 | 24 | Route with a required parameter 25 | 26 | ```php 27 | Route::get('/page/{id}', function ($id) { 28 | return view('page', ['page' => $id]); 29 | }); 30 | 31 | // Using Arrow Functions (Since PHP 7.4) 32 | Route::get('/page/{id}', fn ($id) => view('page', ['page' => $id])); 33 | ``` 34 | 35 | Route with an optional parameter 36 | 37 | ```php 38 | Route::get('/hello/{name?}', function ($name = 'Guest') { 39 | return view('hello', ['name' => $name]); 40 | }); 41 | 42 | // Using Arrow Functions (Since PHP 7.4) 43 | // For optional route parameter {name}, the Closure argument has to have a default value provided 44 | Route::get('/hello/{name?}', fn ($name = 'Guest') => view('hello', ['name' => $name])); 45 | ``` 46 | 47 | Named route (to give the route a name, you would chain a `name()` method call) 48 | 49 | ```php 50 | Route::view('/home')->name('home'); 51 | ``` 52 | 53 | Generating URI of the named route (generating links) 54 | 55 | ```php 56 | // Without parameters 57 | $url = route('home'); // Generates /home 58 | 59 | // With parameters 60 | $blogPostUrl = route('blog-post', ['id' => 1]); // Generates /blog-post/1 61 | ``` 62 | 63 | ### Inside Blade template 64 | Defining a section 65 | 66 | ```blade 67 | @section('content') 68 |

Header

69 | @endsection 70 | ``` 71 | Rendering a section 72 | 73 | ```blade 74 | @yield('content') 75 | ``` 76 | 77 | Extending a layout 78 | 79 | ```blade 80 | @extends('layout') 81 | ``` 82 | 83 | Function to render a view 84 | 85 | ```blade 86 | view('name', ['data' =>‚ 'value']) 87 | ``` 88 | 89 | Rendering data inside a Blade template 90 | 91 | ```blade 92 | {{ $data }} 93 | ``` 94 | 95 | *By default data is escaped using `htmlspecialchars`* 96 | 97 | Rendering unescaped data 98 | 99 | ```blade 100 | {!! $data !!} 101 | ``` 102 | 103 | Including another view 104 | 105 | ```blade 106 | @include('view.name') 107 | ``` 108 | 109 | *Included view will inherit parent view data* 110 | 111 | Passing additional data to included view 112 | 113 | ```blade 114 | @include('view.name', ['name' => 'John']) 115 | ``` 116 | 117 | Generating a URL inside view 118 | 119 | ```blade 120 | Home 121 | ``` 122 | -------------------------------------------------------------------------------- /docs/0026-assets.md: -------------------------------------------------------------------------------- 1 | ### Laravel Mix 2 | 3 | Laravel Mix is a tool on top of [Webpack](https://webpack.js.org/). 4 | It's main purpose is to make life of Laravel developer much easier and does not require you to know Webpack to get started. 5 | 6 | #### What is Webpack? 7 | 8 | Webpack is a module bundler for JavaScript applications. 9 | 10 | The simplest explanation - it converts some input files into output files. 11 | 12 | The input files are one or more JavaScript files, like librariries (React/Vue.js/jQuery and plugins) and your own code. Webpack will then let you to generate a single JavaScript file in a smart way (it will create a dependency graph to know what is used and where). 13 | 14 | #### Installing Node and NPM 15 | 16 | To use Laravel Mix and thus Webpack, you need Node and NPM. 17 | 18 | [Download Node](https://nodejs.org/en/) 19 | 20 | NPM is bundled together with Node. Installation is as easy as it gets (on Windows and Mac), it comes with a UI installer. 21 | 22 | #### What is Node? 23 | 24 | Node.js is a JavaScript runtime environment. It's everything you need to run a JavaScript program outside the browser. 25 | 26 | #### Installing Laravel Mix 27 | 28 | All dependencies are defined inside the `package.json` file. You just need to run 29 | 30 | ``` 31 | npm install 32 | ``` 33 | 34 | The `package.json` contains all JavaScript and CSS libraries your app needs along with their desired version. 35 | 36 | It creates a file called `package-lock.json`. It contains all the libraries that were installed and their version. This file should be included in your project (in GIT repository). 37 | 38 | The difference is simple: 39 | 40 | - You edit `package.json` to say what you need 41 | - `package-lock.json` is auto-generated and describes the versions of libraries installed, and let's every other environment on which your project runs install them using the same exact versions that you have 42 | 43 | `package.json` is like `composer.json` in PHP 44 | `package-lock.json` is like `composer.lock` in PHP 45 | 46 | All the libraries are installed into the `node_modules` folder. 47 | 48 | #### webpack.mix.js 49 | 50 | Laravel Mix configuration file is called `webpack.mix.js`. 51 | 52 | By default it is configured to bundle your application JavaScript and CSS files. 53 | 54 | The main JavaScript file is `resources/js/app.js` 55 | The main CSS file is `resources/sass/app.scss` 56 | 57 | #### Example header 58 | 59 | ``` 60 |
61 |
Laravel Blog
62 | 68 |
69 | ``` -------------------------------------------------------------------------------- /docs/0011-views-collections-dates.md: -------------------------------------------------------------------------------- 1 | ### Loops and including partial templates 2 | 3 | Iterating over elements of collection or array 4 | 5 | ```blade 6 | @foreach ($posts as $post) 7 | {{ $post->title }} 8 | @endforeach 9 | ``` 10 | 11 | Iterating over elements of collection or array and rendering a value when collection is empty (zero length) 12 | 13 | ```blade 14 | @forelse ($posts as $post) 15 | {{ $post->title }} 16 | @empty 17 |

No blog posts

18 | @endforelse 19 | ``` 20 | 21 | Including a sub-view (included views inherit the data of parent view) 22 | 23 | ```blade 24 | @include('post') 25 | ``` 26 | Passing additional data to included view 27 | 28 | ```blade 29 | @include('post', ['some' => 'data']) 30 | ``` 31 | 32 | Including multiple views at once for a collection 33 | 34 | ```blade 35 | @each('post', $posts, 'post') 36 | ``` 37 | 38 | The first argument is the partial view template name, second the collection, third the variable name that would be available inside included view 39 | 40 | ### Conditional rendering 41 | 42 | The @if directive 43 | 44 | ```blade 45 | @if ($post->id === 1) 46 | Post one! 47 | @elseif ($post->id === 2) 48 | Post two! 49 | @else 50 | Something else 51 | @endif 52 | ``` 53 | 54 | The @unless directive 55 | 56 | ```blade 57 | @unless ($post->id === 1) 58 | Post one! 59 | @endunless 60 | ``` 61 | 62 | 63 | ### Dates 64 | 65 | #### Date mutators 66 | 67 | [https://laravel.com/docs/7.x/eloquent-mutators#date-mutators](https://laravel.com/docs/7.x/eloquent-mutators#date-mutators) 68 | 69 | By default the `created_at` and `updated_at` will be converted to Carbon date objects 70 | 71 | It can be configured using `dates` field of the model 72 | 73 | ```php 74 | class BlogPost extends Model 75 | { 76 | protected $dates = [ 77 | 'created_at', 78 | 'updated_at', 79 | 'deleted_at' 80 | ]; 81 | } 82 | ``` 83 | 84 | #### Date format 85 | 86 | By default date columns are stored as `Y-m-d H:i:s` format 87 | 88 | It can be customized using `dateFormat` field of the model. 89 | 90 | **Warning!** This will change how date is stored inside the database and how it is being formatted by default! 91 | 92 | ```php 93 | class BlogPost extends Model 94 | { 95 | protected $dateFormat = 'Y-m-d'; 96 | } 97 | ``` 98 | 99 | #### Carbon 100 | 101 | Carbon is a PHP library that extends the base `\DateTime` class. It's extensively used in Laravel. 102 | 103 | Full API documentation [https://carbon.nesbot.com/docs/](https://carbon.nesbot.com/docs/) 104 | 105 | Formatting the date in the "time ago" format (in Blade template) 106 | 107 | ```blade 108 | {{ $post->created_at->diffForHumans() }} 109 | ``` 110 | 111 | Checking for time difference (https://carbon.nesbot.com/docs/#api-difference) 112 | 113 | In the below example we check if the time passed between now, and the post created_at date is less than 5 minutes. Only then we render the "New!" badge. 114 | 115 | ```blade 116 | @unless (now()->diffInMinutes($post->created_at) < 5) 117 | New! 118 | @endunless 119 | ``` 120 | -------------------------------------------------------------------------------- /docs/0009-models-eloquent.md: -------------------------------------------------------------------------------- 1 | ### Laravel Tinker 2 | 3 | Tinker is a console program that allows you to play around with Laravel models and execute arbitraty PHP command directly from your command line 4 | 5 | Starting the console 6 | 7 | ``` 8 | php artisan tinker 9 | ``` 10 | 11 | #### Solving a problem with Tinker (for PHP 7.3 or newer) when it quits after every command 12 | 13 | Modify the php.ini by adding this option 14 | 15 | ``` 16 | pcre.jit=0 17 | ``` 18 | 19 | On Mac php.ini can be usually found in `/Applications/XAMPP/xamppfiles/etc/php.ini` 20 | 21 | To be sure where it is, run `php -i | grep 'php.ini'` in Terminal 22 | 23 | On Windows it's in your XAMPP folder `C:\xampp\php\php.ini` or `C:\xampp\etc\php.ini`, depending on location of XAMPP itself 24 | 25 | 26 | ### Eloquent Model 27 | 28 | Generating a new model 29 | 30 | ``` 31 | php artisan make:model BlogPost 32 | ``` 33 | 34 | Generating a new model with migration 35 | 36 | ``` 37 | php artisan make:model BlogPost --migration 38 | // or 39 | php artisan make:model BlogPost -m 40 | ``` 41 | 42 | By convention, Laravel assumes the table name is "snake case" plural model name. For example: 43 | 44 | | Model name | Table name | 45 | | -------- |------------- | 46 | | BlogPost | `blog_posts` | 47 | | Blogpost | `blogposts` | 48 | | VeryLongTrain | `very_long_trains` | 49 | 50 | To define a custom name, add (override) the protected `$table` model property 51 | 52 | ``` 53 | class BlogPost extends Model 54 | { 55 | protected $table = 'blogposts'; 56 | } 57 | ``` 58 | 59 | By default, all models are stored inside the `App` namespace, eg. `App\BlogPost` 60 | 61 | #### Acessing and modifying properties 62 | 63 | You can read and modify model properties (row columns) using properties 64 | 65 | ``` 66 | $post = App\BlogPost::find(1); 67 | $title = $post->title; 68 | $content = $post->content; 69 | 70 | $post->title = 'New title'; 71 | $post->content = 'New content'; 72 | // Always call save() to create the record and UPDATE the existing one 73 | $post->save(); 74 | ``` 75 | 76 | Property name corresponds 1:1 to the column name of the table 77 | 78 | ### Querying 79 | 80 | Retrieving all models as *collection* 81 | 82 | ``` 83 | $posts = App\BlogPost::all(); 84 | ``` 85 | 86 | Retrieving single model by primary key (usually, by `id` property/column) 87 | 88 | ``` 89 | // Fetch BlogPost with id 10 90 | $post = App\BlogPost::find(10); 91 | ``` 92 | 93 | Fetching *collection* of models by id 94 | 95 | ``` 96 | $posts = App\BlogPost::find([1, 2, 3]); 97 | ``` 98 | 99 | Collections are iterable (eg. using foreach) 100 | 101 | ``` 102 | $posts = App\BlogPost::all(); 103 | 104 | foreach ($posts as $post) { 105 | echo $post->title; 106 | } 107 | ``` 108 | 109 | Getting first element of the collection of models 110 | 111 | ``` 112 | $posts = App\BlogPost::all(); 113 | $post = $posts->first(); 114 | ``` 115 | 116 | Creating and saving (creating a database row) a new model 117 | 118 | ``` 119 | $post = new App\BlogPost(); 120 | $post->title = 'Title'; 121 | $post->content = 'Content'; 122 | $post->save(); 123 | ``` -------------------------------------------------------------------------------- /docs/0010-resource-controllers.md: -------------------------------------------------------------------------------- 1 | ## Resource controllers 2 | 3 | You can import the model class in your controller 4 | 5 | ```php 6 | use App\BlogPost 7 | 8 | // Now it can be used like this 9 | BlogPost::all(); 10 | // Instead of 11 | \App\BlogPost::all(); 12 | ``` 13 | 14 | ### Resource controllers overview (diagram) 15 | 16 | ![Routing diagram](./../resources/img/Laravel-Resource-Controller.png) 17 | 18 | ### Resource controllers 19 | 20 | If you think about a single resource, like photo - resource controllers let you organize all controller logic around that resource easily 21 | 22 | Using resource controllers 23 | 24 | ```php 25 | Route::resource('posts', 'PostController'); 26 | ``` 27 | 28 | Instead of manually specifying routes 29 | 30 | ```php 31 | Route::get('/posts', 'PostController@index')->name('blog.index'); 32 | Route::get('/posts/{id}', 'PostController@show')->name('blog.show'); 33 | ``` 34 | 35 | Generating a new resource controller with all the resource methods 36 | 37 | `php artisan make:controller PostController --resource` 38 | 39 | Resource methods table 40 | 41 | |Verb | URI |Action |Route Name | 42 | |-------- |-------- |-------- |-------- | 43 | |GET |/posts |index |posts.index | 44 | |GET |/posts/create |create | posts.create | 45 | |POST |/posts |store |posts.store | 46 | |GET |/posts/{photo} |show |posts.show | 47 | |GET |/posts/{photo}/edit |edit |posts.edit | 48 | |PUT/PATCH |/posts/{photo} |update |posts.update | 49 | |DELETE |/posts/{photo} |destroy |posts.destroy | 50 | 51 | To enable only certain routes 52 | 53 | ```php 54 | Route::resource('posts', 'PostController')->only(['index', 'show']); 55 | ``` 56 | 57 | To disable specific routes 58 | 59 | ```php 60 | Route::resource('posts', 'PostController')->except(['create', 'store', 'edit', 'update', 'destroy]); 61 | ``` 62 | 63 | *Both examples above will result in the same routes - posts.index and posts.show* 64 | 65 | ### Fetching a single model 66 | 67 | Model can be fetched using `BlogPost::find($id)` 68 | 69 | To display a 404 Not Found page when model cannot be found, use `BlogPost::findOrFail($id)` 70 | 71 | ### Route Model Binding 72 | 73 | Those 2 examples are equivalent 74 | 75 | ```php 76 | PostController extends Controller { 77 | public function show($post) { 78 | return view('post.show', ['post' => BlogPost::findOrFail($id)]); 79 | } 80 | } 81 | ``` 82 | Above, we manually try to fetch the BlogPost model. `findOrFail` will show a 404 page if model is not found. 83 | 84 | ```php 85 | PostController extends Controller { 86 | public function show(BlogPost $post) { 87 | return view('post.show', ['post' => $post]); 88 | } 89 | } 90 | ``` 91 | Above we use **Route Model Binding**. If the method parameter name matches the route segment, eg. route is `/posts/{post}` the variable name has to be `$post`. Then `type hinting` (specyfying the argument type) as the Eloquent model `BlogPost`, tells Laravel to try and fetch this object by `id`. 92 | 93 | To customize the property by which the model is fetched (using **Route Model Binding**!), add the `getRouteKeyName()` method to the model. 94 | 95 | ```php 96 | public function getRouteKeyName() 97 | { 98 | return 'slug'; 99 | } 100 | ``` 101 | 102 | With the example above, Laravel would try to find a `BlogPost` model by `slug` property. 103 | -------------------------------------------------------------------------------- /docs/0006-migrations.md: -------------------------------------------------------------------------------- 1 | ### Migrations problems 2 | 3 | The limit for keys is 767 bytes. 4 | 5 | In the `utf-8` encoding, the single character is 3 bytes, so key on `VARCHAR(255)` length won't exceed 767 (3 * 255 = 765). 6 | 7 | In you are using `utf8mb4`, which we are, the single character is 4 bytes. Thus the maximum length for `VARCHAR` which has key on it (like `UNIQUE`) is 191. 8 | 9 | When you encounter this: 10 | 11 | ``` 12 | Migration table created successfully. 13 | Migrating: 2014_10_12_000000_create_users_table 14 | 15 | Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`)) 16 | ``` 17 | 18 | Change the email field length to 191. 19 | 20 | ### Running migrations 21 | 22 | To run the most recent, not executed yet migrations 23 | 24 | ``` 25 | php artisan migrate 26 | ``` 27 | 28 | To rollback the most recent migration 29 | 30 | ``` 31 | php artisan migrate:rollback 32 | ``` 33 | 34 | Setting the length of the field 35 | 36 | ``` 37 | $table->string('email', 191); 38 | ``` 39 | 40 | ### Writing migrations 41 | 42 | Creating a new migration 43 | 44 | ``` 45 | php artisan make:migration create_blogposts_table 46 | ``` 47 | 48 | To prefill the migration with stub code that creates a new table 49 | 50 | ``` 51 | php artisan make:migration create_blogposts_table --create=blogposts 52 | ``` 53 | 54 | To prefill the migration with stub code that modifies an existing table 55 | 56 | 57 | ``` 58 | php artisan make:migration add_date_to_blogposts_table --table=blogposts 59 | ``` 60 | 61 | ### The migration file 62 | 63 | Migration file containts a class with 2 methods, `up()` and `down()` 64 | 65 | The `up()` methods creates new table, modifies the existing one. Generally, it specifies how your database schema should change from now on - this might include deleting fields or tables. 66 | 67 | The `down()` method specifies how to revert changes made my migration. 68 | 69 | `up()` method is called when you run `php artisan migrate` 70 | 71 | `down()` method is called when you run `php artisan migrate:rollback` 72 | 73 | Creating a new table 74 | 75 | ``` 76 | Schema::create('blogposts', function (Blueprint $table) { 77 | $table->increments('id'); 78 | }); 79 | ``` 80 | 81 | Renaming an existing table 82 | 83 | ``` 84 | Schema::rename('blogposts', 'posts'); 85 | 86 | ``` 87 | 88 | Creating columns 89 | 90 | ``` 91 | Schema::table('blogposts', function (Blueprint $table) { 92 | $table->string('title'); 93 | }); 94 | ``` 95 | 96 | Available column types 97 | 98 | Refer to this link [https://laravel.com/docs/5.7/migrations#columns](https://laravel.com/docs/5.7/migrations#columns) 99 | 100 | Typical columns types 101 | 102 | | Command | Description | 103 | | -------- |------------- | 104 | | `$table->increments('id');` | Auto-incrementing UNSIGNED INTEGER | 105 | | `$table->string('title', 100);` | VARCHAR with optional length | 106 | | `$table->timestamps();` | Nullable TIMESTAMP `created_at` and `updated_at` columns | 107 | | `$table->text('content');` | TEXT | 108 | 109 | Column modifiers 110 | 111 | `->default('value')` - the default column value 112 | 113 | `->nullable()` - column can be NULL 114 | 115 | `->unsigned()` - integer is unsigned (no negative values) -------------------------------------------------------------------------------- /docs/0028-testing.md: -------------------------------------------------------------------------------- 1 | ### Testing 2 | 3 | You are already set up with Laravel to test your application. 4 | 5 | By default, tests are stored inside `tests` directory. 6 | 7 | It contains two further ones: 8 | 9 | * `Unit` - for testing small isolated parts of your code, like one class functionality or a single method 10 | * `Feature` - as name suggests, testing features of your app, often through making HTTP requests to your running application 11 | 12 | The configuration file `phpunit.xml` is already included. 13 | 14 | #### Testing in a separate environment 15 | 16 | Tests usually run in a separate `environment`. By `environment` I mean different set of settings for your application. 17 | 18 | As you normally store your environment variables inside `.env` file for local development, you would configure this variables inside `phpunit.xml` file instead. 19 | 20 | By default, Laravel runs tests in `testing` environment. 21 | 22 | If your tests involve database, you would not like to perform any changes on your local database. 23 | 24 | You might create a new MySQL database for testing, or use SQLite (an in-memory version of it). 25 | 26 | To specify a different database connection for testing environment, add 27 | 28 | ```xml 29 | 30 | ``` 31 | 32 | Inside the `phpunit.xml`. 33 | 34 | Then, configure the connection inside `config/database.php` file like below 35 | 36 | ```php 37 | 'sqlite_testing' => [ 38 | 'driver' => 'sqlite', 39 | 'database' => ':memory:' 40 | ], 41 | ``` 42 | 43 | The above example would define a new `sqlite_testing` connection, using SQLite. 44 | To create this database in memory, we use the special `database` property value `:memory:` 45 | 46 | Important, before running your tests for the first time, run 47 | 48 | `php artisan config:clear` 49 | 50 | To clear configuration cache. Read more about [configuration cache](https://laravel.com/docs/7.x/configuration#configuration-caching). 51 | 52 | #### Running tests 53 | 54 | To run tests 55 | 56 | `./vendor/bin/phpunit` 57 | 58 | #### Writing tests 59 | 60 | All your tests will extend the base class `TestCase` from `Tests` namespace. 61 | 62 | Here is an example `Unit` test 63 | 64 | ```php 65 | assertTrue(true); 76 | } 77 | } 78 | ``` 79 | 80 | All testing method names should start with `test`, eg. `testHome`, `testContact`, `testPostStored` etc. 81 | 82 | A single test can be divided into three parts 83 | 84 | * `Arrange` - test set up, like creating an object to be tested or mocks 85 | * `Act` - execute the objects created in the `Arrange` step 86 | * `Assert` - check and verify the result from the `Act` step against expected results 87 | 88 | #### Database 89 | 90 | To refresh database after each test (rebuild schema using migrations), add the `Illuminate\Foundation\Testing\RefreshDatabase` trait to your test class. 91 | 92 | An example: 93 | 94 | ```php 95 | get('/posts'); 110 | 111 | $response->assertSeeText('No blog posts yet!'); 112 | } 113 | } 114 | ``` 115 | 116 | #### Testing using HTTP requests 117 | 118 | You can call your application using HTTP 119 | 120 | ```php 121 | $response = $this->get('/posts'); 122 | ``` 123 | 124 | Then, you can perform some assertions on the result, like checking if certain text was displayed or if JSON with specific values was returned. 125 | 126 | ```php 127 | $response->assertSeeText('First post'); 128 | ``` 129 | 130 | Here is a list of all [possible assertions](https://laravel.com/docs/7.x/http-tests#available-assertions) 131 | 132 | -------------------------------------------------------------------------------- /docs/0012-form-saving.md: -------------------------------------------------------------------------------- 1 | ### Forms 2 | 3 | #### CSRF Protection 4 | 5 | In Laravel 5.7 you create form by simple HTML 6 | 7 | ``` 8 |
9 | 10 |
11 | ``` 12 | 13 | [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection is enabled by default, so you need to include a CSRF token with each form sent 14 | 15 | Token can be included by adding a `@csrf` directive inside the `
` tag. It would generate an `` of type `hidden` with the token as value 16 | 17 | ``` 18 | 19 | @csrf 20 |
21 | ``` 22 | 23 | The token is then verified inside Laravel using the `VerifyCsrfToken` middleware. 24 | 25 | ### Middleware 26 | 27 | Middleware is a mechanism that filter requests going through your application. 28 | 29 | Simply put - each middleware is a chunk of code that runs BEFORE or AFTER the request is handled by `Controller Action` or a `Closure`. 30 | 31 | Below is an example flow of the request going through your application: 32 | 33 | ![Request flow with middleware](./../resources/img/middleware.png) 34 | 35 | An example AFTER middleware from [Laravel Docs](https://laravel.com/docs/5.7/middleware#defining-middleware) 36 | 37 | ``` 38 | namespace App\Http\Middleware; 39 | 40 | use Closure; 41 | 42 | class AfterMiddleware 43 | { 44 | public function handle($request, Closure $next) 45 | { 46 | // Calling $next with $request parameter 47 | $response = $next($request); 48 | 49 | // Do something here after the request is handled by Controller/Closure 50 | 51 | return $response; 52 | } 53 | } 54 | ``` 55 | 56 | An example BEFORE middleware 57 | 58 | ``` 59 | namespace App\Http\Middleware; 60 | 61 | use Closure; 62 | 63 | class BeforeMiddleware 64 | { 65 | public function handle($request, Closure $next) 66 | { 67 | // Do something here before the request is handled by Controller/Closure... 68 | 69 | // Calling $next with $request parameter 70 | return $next($request); 71 | } 72 | } 73 | ``` 74 | 75 | Middleware should call the passed `Closure` `$next` with the `$request` parameter to allow further processing, or `throw` an `Exception` or do a redirect to stop further processing of the `Request`. 76 | 77 | Middleware examples: 78 | 79 | * Authentication (veryfying if user is authenticated) 80 | * CSRF protection 81 | * CORS middleware 82 | 83 | ### Request 84 | 85 | Obtaining data sent with request 86 | 87 | ``` 88 | class PostController extends Controller 89 | { 90 | public function store(Request $request) 91 | { 92 | $title = $request->input('title'); 93 | } 94 | } 95 | ``` 96 | 97 | Reading all input as an `array` 98 | 99 | ``` 100 | $input = $request->all(); 101 | ``` 102 | 103 | Reading an individual value with default provided 104 | 105 | ``` 106 | $name = $request->input('title', 'Draft post'); 107 | ``` 108 | 109 | Retrieving all of the input values as an `array` 110 | 111 | ``` 112 | $input = $request->input(); 113 | ``` 114 | 115 | The `input()` method can read data regardless of the HTTP verb used (works for `GET` query parameters or input fields sent through `
` with `POST` method) 116 | 117 | Veryfing if input value is present 118 | 119 | ``` 120 | if ($request->has('title')) { 121 | // Do something with title 122 | } 123 | ``` 124 | 125 | ### Redirects 126 | 127 | Redirect to a URL 128 | 129 | ``` 130 | return redirect('/posts'); 131 | ``` 132 | 133 | Redirect to a route with "flashed input" 134 | 135 | ``` 136 | return redirect('/posts/create')->withInput(); 137 | ``` 138 | 139 | Redirect to last URL with "flashed input" 140 | 141 | ``` 142 | return back()->withInput(); 143 | ``` 144 | 145 | Redirect to a named route 146 | 147 | ``` 148 | return redirect()->route('posts.index'); 149 | ``` 150 | 151 | Redirect with flash message 152 | 153 | ``` 154 | return redirect()->route('posts')->with('status', 'New post created!'); 155 | ``` 156 | 157 | #### Flashing input 158 | 159 | Sometimes you need to repopulate the form with the old input, eg. when validation failed and you don't want the user to re-type all the fields. 160 | 161 | You can do 162 | 163 | ``` 164 | $request->flash(); 165 | ``` 166 | 167 | Or the above example (flash input and redirect) 168 | 169 | ``` 170 | return redirect('form')->withInput(); 171 | ``` -------------------------------------------------------------------------------- /docs/0036-one-to-one.md: -------------------------------------------------------------------------------- 1 | ## One-to-One relations 2 | 3 | An example one-to-one relation diagram: 4 | 5 | ![Graph](./../resources/img/one-to-one.png) 6 | 7 | ## Database design 8 | 9 | ![](./../resources/img/one-to-one-database.png) 10 | 11 | With one-to-one relation, 1 record from first table (eg. `authors`) has exactly 1 matching record on the other table (eg. `profiles`). 12 | 13 | One of the tables needs to have a column that will hold the `id` of the record on the other table. 14 | 15 | In our example, we have `authors` and `profiles`. Since it makes no sense for a `Profile` to exist on it's own, the relation column (`author_id`) is put on the `profiles` table. 16 | 17 | In short: 18 | 19 | * has* - methods on Models that own the relation, no relation column 20 | * belongs* - methods on Models that are other side of the relation, with relation column 21 | 22 | In the example above each `Author` model has one `Profile`. 23 | 24 | ## Defining relations 25 | 26 | The relation on `Profile` model: 27 | 28 | ``` 29 | class Profile extends Model 30 | { 31 | public function author() 32 | { 33 | return $this->belongsTo('App\Author'); 34 | } 35 | } 36 | ``` 37 | 38 | Relation on `Author` model: 39 | 40 | ``` 41 | class Author extends Model 42 | { 43 | public function profile() 44 | { 45 | return $this->hasOne('App\Profile'); 46 | } 47 | } 48 | ``` 49 | 50 | ## Migrations 51 | 52 | `Author` model migration: 53 | 54 | ``` 55 | Schema::create('authors', function (Blueprint $table) { 56 | $table->increments('id'); 57 | $table->timestamps(); 58 | }); 59 | ``` 60 | 61 | `Profile` model migration: 62 | 63 | ``` 64 | Schema::create('profiles', function (Blueprint $table) { 65 | $table->increments('id'); 66 | $table->timestamps(); 67 | 68 | $table->unsignedInteger('author_id')->unique(); 69 | $table->foreign('author_id')->references('id')->on('authors'); 70 | }); 71 | ``` 72 | 73 | ## Accessing related object 74 | 75 | 1) Accessing the relation on already loaded model 76 | 77 | ``` 78 | use App\Author; 79 | 80 | $author = Author::find(1); // Load author model 81 | $profile = $author->profile; // Load the relation (separate query is made) 82 | 83 | $profile = Profile::find(1); 84 | $author = $profile->author; 85 | ``` 86 | 87 | The `method` name defining a relation is accessed as a `field` with the same name as the `method` name. 88 | 89 | This is called `Lazy Loading`. Relations are only loaded from database when they are accessed for the 1st time. 90 | 91 | 2) Loading the model with 1 relation at once 92 | 93 | ``` 94 | use App\Author; 95 | 96 | $author = Author::with('profile')->whereKey(1)->first(); 97 | ``` 98 | 99 | The `Model::find()` is a actually a shortcut [view source](https://github.com/laravel/framework/blob/5.7/src/Illuminate/Database/Eloquent/Builder.php#L323) 100 | 101 | That behind the scenes does this: 102 | 103 | ``` 104 | return $this->whereKey($id)->first($columns); 105 | ``` 106 | 107 | 3) Loading the model with many relations at once 108 | 109 | ``` 110 | use App\Author; 111 | 112 | $author = Author::with(['profile', 'account'])->whereKey(1)->get(); 113 | ``` 114 | 115 | ## Creating association 116 | 117 | ``` 118 | $author = new Author(); 119 | $author->save(); 120 | 121 | $profile = new Profile(); 122 | 123 | $author->profile()->save($profile); 124 | ``` 125 | 126 | Notes: 127 | 128 | - You can't save `Profile` model before `author_id` column is assigned with the valid `id` of `Author` 129 | - That means, you can't use `Profile::create()` as it immediately saves the model, unless you pass the `author_id` like `Profile::create(['author_id' => 1]);` 130 | - The latter can only be used, when `author_id` is on the `$fillable` list of the `Profile` model 131 | 132 | ``` 133 | $profile = new Profile(); 134 | $author = Author::create(); 135 | 136 | $profile->author()->associate($author)->save(); 137 | ``` 138 | 139 | Or 140 | 141 | ``` 142 | $profile = new Profile(); // Create model instance 143 | $profile->author_id = 1; // Create relationship 144 | $profile->save(); // Save model 145 | ``` 146 | 147 | Or 148 | 149 | ``` 150 | // Create new model with relationship and save 151 | $profile = Profile::create(['author_id' => 1]); 152 | ``` 153 | 154 | In the end, creating the association is basically assigning the `author_id` column of the `Profile` model (`profiles` table record). 155 | --------------------------------------------------------------------------------