yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    laravel 5.7 from Scratch.

    laravelЛекции по laravel от Scratch. Конспект. Эпизоды 8-. Всё по англицки - ни хера не понимаю.. буду излагать лапидарно.

    конспектировал Капустин Яков

    оглавление

    bash:
    vagrant@homestead:~/projects$ composer create-project --prefer-dist laravel/laravel scratch Installing laravel/laravel (v5.7.19) - Installing laravel/laravel (v5.7.19): Loading from cache Created project in test > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 86 installs, 0 updates, 0 removals - Installing symfony/polyfill-ctype (v1.10.0): Loading from cache - Installing vlucas/phpdotenv (v2.6.1): Loading from cache - Installing symfony/css-selector (v4.2.3): Loading from cache - Installing tijsverkoyen/css-to-inline-styles (2.2.1): Loading from cache - Installing symfony/polyfill-php72 (v1.10.0): Loading from cache - Installing symfony/polyfill-mbstring (v1.10.0): Loading from cache - Installing symfony/var-dumper (v4.2.3): Loading from cache - Installing symfony/routing (v4.2.3): Loading from cache - Installing symfony/process (v4.2.3): Loading from cache - Installing symfony/http-foundation (v4.2.3): Loading from cache - Installing symfony/contracts (v1.0.2): Loading from cache - Installing symfony/event-dispatcher (v4.2.3): Loading from cache - Installing psr/log (1.1.0): Loading from cache - Installing symfony/debug (v4.2.3): Loading from cache - Installing symfony/http-kernel (v4.2.3): Loading from cache - Installing symfony/finder (v4.2.3): Loading from cache - Installing symfony/console (v4.2.3): Loading from cache - Installing doctrine/lexer (v1.0.1): Loading from cache - Installing egulias/email-validator (2.1.7): Loading from cache - Installing swiftmailer/swiftmailer (v6.1.3): Loading from cache - Installing paragonie/random_compat (v9.99.99): Loading from cache - Installing ramsey/uuid (3.8.0): Loading from cache - Installing psr/simple-cache (1.0.1): Loading from cache - Installing psr/container (1.0.0): Loading from cache - Installing opis/closure (3.1.5): Loading from cache - Installing symfony/translation (v4.2.3): Loading from cache - Installing nesbot/carbon (1.36.2): Loading from cache - Installing monolog/monolog (1.24.0): Loading from cache - Installing league/flysystem (1.0.50): Loading from cache - Installing erusev/parsedown (1.7.1): Loading from cache - Installing dragonmantank/cron-expression (v2.2.0): Loading from cache - Installing doctrine/inflector (v1.3.0): Loading from cache - Installing ralouphie/getallheaders (2.0.5): Loading from cache - Installing psr/http-message (1.0.1): Loading from cache - Installing guzzlehttp/psr7 (1.5.2): Loading from cache - Installing guzzlehttp/promises (v1.3.1): Loading from cache - Installing guzzlehttp/guzzle (6.3.3): Loading from cache - Installing laravel/slack-notification-channel (v1.0.3): Loading from cache - Installing laravel/framework (v5.7.26): Downloading (100%) - Installing lcobucci/jwt (3.2.5): Loading from cache - Installing php-http/promise (v1.0.0): Loading from cache - Installing php-http/httplug (v1.1.0): Loading from cache - Installing php-http/guzzle6-adapter (v1.1.1): Loading from cache - Installing zendframework/zend-diactoros (1.8.6): Loading from cache - Installing nexmo/client (1.6.2): Loading from cache - Installing laravel/nexmo-notification-channel (v1.0.1): Loading from cache - Installing fideloper/proxy (4.1.0): Loading from cache - Installing nikic/php-parser (v4.2.0): Loading from cache - Installing jakub-onderka/php-console-color (v0.2): Loading from cache - Installing jakub-onderka/php-console-highlighter (v0.4): Loading from cache - Installing dnoegel/php-xdg-base-dir (0.1): Loading from cache - Installing psy/psysh (v0.9.9): Loading from cache - Installing laravel/tinker (v1.0.8): Loading from cache - Installing beyondcode/laravel-dump-server (1.2.2): Loading from cache - Installing fzaninotto/faker (v1.8.0): Loading from cache - Installing hamcrest/hamcrest-php (v2.0.0): Loading from cache - Installing mockery/mockery (1.2.2): Downloading (100%) - Installing filp/whoops (2.3.1): Loading from cache - Installing nunomaduro/collision (v2.1.1): Loading from cache - Installing sebastian/version (2.0.1): Loading from cache - Installing sebastian/resource-operations (2.0.1): Loading from cache - Installing sebastian/object-reflector (1.1.1): Loading from cache - Installing sebastian/recursion-context (3.0.0): Loading from cache - Installing sebastian/object-enumerator (3.0.3): Loading from cache - Installing sebastian/global-state (2.0.0): Loading from cache - Installing sebastian/exporter (3.1.0): Loading from cache - Installing sebastian/environment (4.1.0): Loading from cache - Installing sebastian/diff (3.0.2): Loading from cache - Installing sebastian/comparator (3.0.2): Loading from cache - Installing phpunit/php-timer (2.0.0): Loading from cache - Installing phpunit/php-text-template (1.2.1): Loading from cache - Installing phpunit/php-file-iterator (2.0.2): Loading from cache - Installing theseer/tokenizer (1.1.0): Loading from cache - Installing sebastian/code-unit-reverse-lookup (1.0.1): Loading from cache - Installing phpunit/php-token-stream (3.0.1): Loading from cache - Installing phpunit/php-code-coverage (6.1.4): Loading from cache - Installing doctrine/instantiator (1.1.0): Loading from cache - Installing webmozart/assert (1.4.0): Loading from cache - Installing phpdocumentor/reflection-common (1.0.1): Loading from cache - Installing phpdocumentor/type-resolver (0.4.0): Loading from cache - Installing phpdocumentor/reflection-docblock (4.3.0): Loading from cache - Installing phpspec/prophecy (1.8.0): Loading from cache - Installing phar-io/version (2.0.1): Loading from cache - Installing phar-io/manifest (1.0.3): Loading from cache - Installing myclabs/deep-copy (1.8.1): Loading from cache - Installing phpunit/phpunit (7.5.5): Downloading (100%) symfony/routing suggests installing doctrine/annotations (For using the annotation loader) symfony/routing suggests installing symfony/config (For using the all-in-one router or any loader) symfony/routing suggests installing symfony/dependency-injection (For loading routes from a service) symfony/routing suggests installing symfony/expression-language (For using expression matching) symfony/routing suggests installing symfony/yaml (For using the YAML loader) symfony/contracts suggests installing psr/cache (When using the Cache contracts) symfony/contracts suggests installing symfony/cache-contracts-implementation symfony/contracts suggests installing symfony/service-contracts-implementation symfony/event-dispatcher suggests installing symfony/dependency-injection symfony/http-kernel suggests installing symfony/browser-kit symfony/http-kernel suggests installing symfony/config symfony/http-kernel suggests installing symfony/dependency-injection symfony/console suggests installing symfony/lock swiftmailer/swiftmailer suggests installing true/punycode (Needed to support internationalized email addresses, if ext-intl is not installed) paragonie/random_compat suggests installing ext-libsodium (Provides a modern crypto API that can be used to generate random bytes.) ramsey/uuid suggests installing ircmaxell/random-lib (Provides RandomLib for use with the RandomLibAdapter) ramsey/uuid suggests installing ext-libsodium (Provides the PECL libsodium extension for use with the SodiumRandomGenerator) ramsey/uuid suggests installing ext-uuid (Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator) ramsey/uuid suggests installing moontoast/math (Provides support for converting UUID to 128-bit integer (in string form).) ramsey/uuid suggests installing ramsey/uuid-doctrine (Allows the use of Ramsey\Uuid\Uuid as Doctrine field type.) ramsey/uuid suggests installing ramsey/uuid-console (A console application for generating UUIDs with ramsey/uuid) symfony/translation suggests installing symfony/config symfony/translation suggests installing symfony/yaml nesbot/carbon suggests installing friendsofphp/php-cs-fixer (Needed for the `composer phpcs` command. Allow to automatically fix code style.) nesbot/carbon suggests installing phpstan/phpstan (Needed for the `composer phpstan` command. Allow to detect potential errors.) monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB) monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server) monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required)) monolog/monolog suggests installing ext-mongo (Allow sending log messages to a MongoDB server) monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server) monolog/monolog suggests installing mongodb/mongodb (Allow sending log messages to a MongoDB server via PHP Driver) monolog/monolog suggests installing php-amqplib/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib) monolog/monolog suggests installing php-console/php-console (Allow sending log messages to Google Chrome) monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar) monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server) monolog/monolog suggests installing sentry/sentry (Allow sending log messages to a Sentry server) league/flysystem suggests installing league/flysystem-aws-s3-v2 (Allows you to use S3 storage with AWS SDK v2) league/flysystem suggests installing league/flysystem-aws-s3-v3 (Allows you to use S3 storage with AWS SDK v3) league/flysystem suggests installing league/flysystem-azure (Allows you to use Windows Azure Blob storage) league/flysystem suggests installing league/flysystem-cached-adapter (Flysystem adapter decorator for metadata caching) league/flysystem suggests installing league/flysystem-eventable-filesystem (Allows you to use EventableFilesystem) league/flysystem suggests installing league/flysystem-rackspace (Allows you to use Rackspace Cloud Files) league/flysystem suggests installing league/flysystem-sftp (Allows you to use SFTP server storage via phpseclib) league/flysystem suggests installing league/flysystem-webdav (Allows you to use WebDAV storage) league/flysystem suggests installing league/flysystem-ziparchive (Allows you to use ZipArchive adapter) league/flysystem suggests installing spatie/flysystem-dropbox (Allows you to use Dropbox storage) league/flysystem suggests installing srmklive/flysystem-dropbox-v2 (Allows you to use Dropbox storage for PHP 5 applications) laravel/framework suggests installing aws/aws-sdk-php (Required to use the SQS queue driver and SES mail driver (^3.0).) laravel/framework suggests installing doctrine/dbal (Required to rename columns and drop SQLite columns (^2.6).) laravel/framework suggests installing league/flysystem-aws-s3-v3 (Required to use the Flysystem S3 driver (^1.0).) laravel/framework suggests installing league/flysystem-cached-adapter (Required to use the Flysystem cache (^1.0).) laravel/framework suggests installing league/flysystem-rackspace (Required to use the Flysystem Rackspace driver (^1.0).) laravel/framework suggests installing league/flysystem-sftp (Required to use the Flysystem SFTP driver (^1.0).) laravel/framework suggests installing moontoast/math (Required to use ordered UUIDs (^1.1).) laravel/framework suggests installing pda/pheanstalk (Required to use the beanstalk queue driver (^3.0).) laravel/framework suggests installing predis/predis (Required to use the redis cache and queue drivers (^1.0).) laravel/framework suggests installing pusher/pusher-php-server (Required to use the Pusher broadcast driver (^3.0).) laravel/framework suggests installing symfony/dom-crawler (Required to use most of the crawler integration testing tools (^4.1).) laravel/framework suggests installing symfony/psr-http-message-bridge (Required to psr7 bridging features (^1.0).) lcobucci/jwt suggests installing mdanter/ecc (Required to use Elliptic Curves based algorithms.) psy/psysh suggests installing ext-pdo-sqlite (The doc command requires SQLite to work.) psy/psysh suggests installing hoa/console (A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit.) filp/whoops suggests installing whoops/soap (Formats errors as SOAP responses) sebastian/global-state suggests installing ext-uopz (*) phpunit/php-code-coverage suggests installing ext-xdebug (^2.6.0) phpunit/phpunit suggests installing phpunit/php-invoker (^2.0) phpunit/phpunit suggests installing ext-xdebug (*) Writing lock file Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: beyondcode/laravel-dump-server Discovered Package: fideloper/proxy Discovered Package: laravel/nexmo-notification-channel Discovered Package: laravel/slack-notification-channel Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. > @php artisan key:generate --ansi Application key set successfully. vagrant@homestead:~/projects$

    В новом приложении 'scratch' (автор создаёт его с помощью команды 'laravel new project' на 16 секунде 7 эпизода) меняем базу данных на 'scratch_db' и создаём миграцию.

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan help make:migration Description: Create a new migration file Usage: make:migration [options] [--] Arguments: name The name of the migration Options: --create[=CREATE] The table to be created --table[=TABLE] The table to migrate --path[=PATH] The location where the migration file should be created --realpath Indicate any provided migration file paths are pre-resolved absolute paths -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --env[=ENV] The environment the command should run under -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug vagrant@homestead:~/projects/scratch$

    Создаём миграцию 'create_projects_table':

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:migration create_projects_table Created Migration: 2019_02_08_235353_create_projects_table vagrant@homestead:~/projects/scratch$

    и добавляем в метод 'up' создание дополнительных полей 'title' и 'description'.

    scratch/database/migrations/2019_02_08_235353_create_projects_table.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

      
    <?php

      
    use Illuminate\Support\Facades\Schema;
      use 
    Illuminate\Database\Schema\Blueprint;
      use 
    Illuminate\Database\Migrations\Migration;

      class 
    CreateProjectsTable extends Migration
      
    {
          
    /**
           * Run the migrations.
           *
           * @return void
           */
          
    public function up()
          {
              
    Schema::create('projects', function (Blueprint $table) {
                  
    $table->increments('id');
                  
    $table->string('title');
                  
    $table->string('description');
                  
    $table->timestamps();
              });
          }

          
    /**
           * Reverse the migrations.
           *
           * @return void
           */
          
    public function down()
          {
              
    Schema::dropIfExists('projects');
          }
      }
      
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan migrate Migrating: 2019_02_08_235353_create_projects_table Migrated: 2019_02_08_235353_create_projects_table vagrant@homestead:~/projects/scratch$

    Эпизод 8. Ахтунг! Имя модели связано с именем созданной ранее миграции. Смотреть предыдущую лекцию, в которой её создали.

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:model Project Model created successfully. vagrant@homestead:~/projects/scratch$

    Laravel включает в себя мощный REPL (Read—Eval—Print—Loop) инструмент - Tinker, работающий под управлением PsySH. Tinker позволяет вам взаимодействовать с вашим laravel приложением из командной строки.

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> 5-3 => 2 >>> App\Project::all(); => Illuminate\Database\Eloquent\Collection {#2908 all: [], } >>> // просмотр всех записей >>> // пока здесь пусто >>> >>> App\Project::first(); => null >>> App\Project::latest()->first(); => null >>>

    Создаём экземпляр класса App\Project и присваиваем ему 'title' и 'description'.

    bash:
    >>> $project=new App\Project; => App\Project {#2906} >>> $project->title='My first Project' => "My first Project" >>> // забыл ';', но tinker почему-то не ругнулся.. >>> >>> $project->description='Lorem Ipsum'; => "Lorem Ipsum" >>> >>> // запускаем?? >>> $project => App\Project {#2906 title: "My first Project", description: "Lorem Ipsum", } >>> >>> $project->save(); => true >>> >>> exit Exit: Goodbye vagrant@homestead:~/projects/scratch$ mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 157 Server version: 5.7.25-0ubuntu0.18.04.2 (Ubuntu) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use scratch_db; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from project; ERROR 1146 (42S02): Table 'scratch_db.project' doesn't exist mysql> show tables; +----------------------+ | Tables_in_scratch_db | +----------------------+ | migrations | | password_resets | | projects | | users | +----------------------+ 4 rows in set (0.00 sec) mysql> select * from projects; +----+------------------+-------------+---------------------+---------------------+ | id | title | description | created_at | updated_at | +----+------------------+-------------+---------------------+---------------------+ | 1 | My first Project | Lorem Ipsum | 2019-02-09 01:01:29 | 2019-02-09 01:01:29 | +----+------------------+-------------+---------------------+---------------------+ 1 row in set (0.00 sec) mysql> Bye vagrant@homestead:~/projects/scratch$
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> App\Project::first(); => App\Project {#2911 id: 1, title: "My first Project", description: "Lorem Ipsum", created_at: "2019-02-09 01:01:29", updated_at: "2019-02-09 01:01:29", } >>> >>> App\Project::first()->title; => "My first Project" >>> App\Project::first()->description; => "Lorem Ipsum" >>> >>> App\Project::all(); => Illuminate\Database\Eloquent\Collection {#2906 all: [ App\Project {#2905 id: 1, title: "My first Project", description: "Lorem Ipsum", created_at: "2019-02-09 01:01:29", updated_at: "2019-02-09 01:01:29", }, ], } >>> >>> >>> >>> $project=new App\Project; => App\Project {#2911} >>> $project->title='Second Project' => "Second Project" >>> $project->description='description second project' => "description second project" >>> App\Project::all(); => Illuminate\Database\Eloquent\Collection {#2896 all: [ App\Project {#2900 id: 1, title: "My first Project", description: "Lorem Ipsum", created_at: "2019-02-09 01:01:29", updated_at: "2019-02-09 01:01:29", }, ], } >>> $project->save(); => true >>> App\Project::all(); => Illuminate\Database\Eloquent\Collection {#2913 all: [ App\Project {#2902 id: 1, title: "My first Project", description: "Lorem Ipsum", created_at: "2019-02-09 01:01:29", updated_at: "2019-02-09 01:01:29", }, App\Project {#2914 id: 2, title: "Second Project", description: "description second project", created_at: "2019-02-09 01:17:54", updated_at: "2019-02-09 01:17:54", }, ], } >>> >>> >>> App\Project::all()[0]; => App\Project {#2916 id: 1, title: "My first Project", description: "Lorem Ipsum", created_at: "2019-02-09 01:01:29", updated_at: "2019-02-09 01:01:29", } >>> App\Project::all()[1]; => App\Project {#2906 id: 2, title: "Second Project", description: "description second project", created_at: "2019-02-09 01:17:54", updated_at: "2019-02-09 01:17:54", } >>> App\Project::all()[2]; PHP Notice: Undefined offset: 2 in /home/vagrant/projects/scratch/vendor/laravel/framework/src/Illuminate/Support/Collection.php on line 1924 >>> App\Project::all()[1]->title; => "Second Project" >>> App\Project::all()-map->title; PHP Parse error: Syntax error, unexpected T_OBJECT_OPERATOR on line 1 >>> App\Project::all()->map->title; => Illuminate\Support\Collection {#2916 all: [ "My first Project", "Second Project", ], } >>>

    Создадим новый маршрут:

    routes/web.php:
    1
    2
    3
    4
    5

      
    ...
      
    Route::get('/projects''ProjectsController@index');
      ...
      

    .. новый контроллер:

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:controller ProjectsController Controller created successfully. vagrant@homestead:~/projects/scratch$
    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    Illuminate\Http\Request;

      class 
    ProjectsController extends Controller
      
    {
          public function 
    index(){
              return 
    view('projects.index');
          }
      }
      

    и новый вид:

    resources/views/projects/index.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17

      
    <!DOCTYPE html>
      <
    html lang="en">
      <
    head>
          <
    meta charset="UTF-8">
          <
    meta name="viewport" content="width=device-width, initial-scale=1.0">
          <
    meta http-equiv="X-UA-Compatible" content="ie=edge">
          <
    title>Document</title>
      </
    head>
      <
    body>
          <
    h1>New Project</h1>
          <?
    php
              
    echo __FILE__;
          
    ?>
      </body>
      </html>
      

    Перейдём по ссылке http://laravel.scratch/projects:

    Рис. 1 project mvc
    Рис. 1 project mvc

    Добавим в метод созданного контроллера немного логики, вместо вида возвратив значение переменной $project:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    Illuminate\Http\Request;
      use \
    App\Project;

      class 
    ProjectsController extends Controller
      
    {
          public function 
    index(){

              
    // $project = \App\Project::all();
              // после добавления пространства имен:
              // use \App\Project;
              // можно использовать запись ниже
              
    $project Project::all();

              
    //return view('projects.index');
              
    return $project;

          }
      }
      

    На выходе мы получим json, для читабельного отображения которого установим в браузере расширение JSON Formatter (Offered by: Nik Rolls):

    Рис. 2 вывод json в браузере
    Рис. 2 вывод json в браузере

    Теперь изменим код контроллера, вернув вид, передав в него во втором аргументе массив полученных объектов:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    Illuminate\Http\Request;
      use \
    App\Project;

      class 
    ProjectsController extends Controller
      
    {
          public function 
    index(){

              
    // $project = \App\Project::all();
              // после добавления пространства имен:
              // use \App\Project;
              // можно использовать запись ниже
              
    $projects Project::all();

              
    // return view('projects.index', ['projects'=>$projects]);
              
    return view('projects.index'compact('projects'));

          }
      }
      

    Добавим во view вызов переданного аргумента:

    resources/views/projects/index.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

      
    <!DOCTYPE html>
      <
    html lang="en">
      <
    head>
          <
    meta charset="UTF-8">
          <
    meta name="viewport" content="width=device-width, initial-scale=1.0">
          <
    meta http-equiv="X-UA-Compatible" content="ie=edge">
          <
    title>Document</title>
      </
    head>
      <
    body>
          <
    h1>New Project</h1>
          <?
    php
              
    // echo __FILE__;
          
    ?>
          @foreach($projects as $project)
              <li>{{ $project->title }}</li>
          @endforeach
      </body>
      </html>
      
    Рис. 3 New Project
    Рис. 3 New Project
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan inspire Simplicity is the ultimate sophistication. - Leonardo da Vinci vagrant@homestead:~/projects/scratch$ php artisan inspire Well begun is half done. - Aristotle vagrant@homestead:~/projects/scratch$ php artisan inspire It is quality rather than quantity that matters. - Lucius Annaeus Seneca vagrant@homestead:~/projects/scratch$
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> >>> factory(App\User::class)->make(); => App\User {#2931 name: "Kayley Schuster", email: "meda18@example.com", email_verified_at: Illuminate\Support\Carbon @1549712891 {#2927 date: 2019-02-09 11:48:11.198086 UTC (+00:00), }, } >>> factory(App\User::class)->make(); => App\User {#2925 name: "Mr. Johnny Sporer Sr.", email: "tracey66@example.org", email_verified_at: Illuminate\Support\Carbon @1549712896 {#2926 date: 2019-02-09 11:48:16.349419 UTC (+00:00), }, } >>> factory(App\User::class)->make(); => App\User {#2932 name: "Alfonzo McKenzie", email: "ellsworth.carter@example.com", email_verified_at: Illuminate\Support\Carbon @1549712899 {#2924 date: 2019-02-09 11:48:19.087973 UTC (+00:00), }, } >>> factory(App\User::class)->create(); => App\User {#2930 name: "Lonie Baumbach", email: "brianne77@example.org", email_verified_at: Illuminate\Support\Carbon @1549713020 {#2922 date: 2019-02-09 11:50:20.175508 UTC (+00:00), }, updated_at: "2019-02-09 11:50:20", created_at: "2019-02-09 11:50:20", id: 1, } >>> exit Exit: Goodbye vagrant@homestead:~/projects/scratch$ mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 168 Server version: 5.7.25-0ubuntu0.18.04.2 (Ubuntu) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use scratch_db; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from users; +----+----------------+-----------------------+---------------------+------------+----------------+---------------------+---------------------+ | id | name | email | email_verified_at | password | remember_token | created_at | updated_at | +----+----------------+-----------------------+---------------------+------------+----------------+---------------------+---------------------+ | 1 | Lonie Baumbach | brianne77@example.org | 2019-02-09 11:50:20 | $2y$10$T.. | pZb9HTRaWR | 2019-02-09 11:50:20 | 2019-02-09 11:50:20 | +----+----------------+-----------------------+---------------------+------------+----------------+---------------------+---------------------+ 1 row in set (0.00 sec) mysql>

    Эпизод 10. Добавим маршруты на создаваемую страницу с формой и для сохранения данных в базу:

    routes/web.php:
    1
    2
    3
    4
    5
    6
    7
    8

      
    ...
      
    Route::get('/projects''ProjectsController@index');
      
    Route::post('/projects''ProjectsController@save');

      
    Route::get('/projects/create''ProjectsController@create');
      ...
      

    В контроллере пропишем новый метод:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    ...
      public function 
    save(){

          
    $project=new Project;

          
    $project->title=request('title');
          
    $project->description=request('description');

          
    $project->save();

          return 
    redirect('/projects');
      }
      ...
      

    И создадим новый вид, в котором создадим форму с action='/project' и скрытыми полями для защиты от CSRF-атаки и передачи метода. Ниже представлены равноценные способы:

    resources/views/projects/edit.blade.php:
    1
    2
    3
    4
    5
    6
    7

      
    ...
      <
    form>
        {{ 
    csrf_field() }}
        {{ 
    method_field('DELETE') }}
      ...
      
    resources/views/projects/edit.blade.php:
    1
    2
    3
    4
    5
    6
    7

      
    ...
      <
    form>
        @
    csrf
        
    @method('DELETE')
      ...
      
    resources/views/projects/create.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

      
    <!DOCTYPE html>
      <
    html lang="en">
      <
    head>
          <
    meta charset="UTF-8">
          <
    meta name="viewport" content="width=device-width, initial-scale=1.0">
          <
    meta http-equiv="X-UA-Compatible" content="ie=edge">
          <
    title>Document</title>
      </
    head>
      <
    body>
          <
    h1>Create Project</h1>
          <
    form action='/projects' method='post'>
              {{ 
    csrf_field() }}
              
    Title Project: <input type='text' name='title'><br>
              
    Description Project: <input type='text' name='description'><br>
              <
    input type='submit' value='Submit'>
          </
    form>
      </
    body>
      </
    html>
      

    Заменим объявление маршрута на следующую конструкцию, обеспечивающую создание типичных роутов:

    routes/web.php:
    1
    2
    3
    4
    5
    6

      
    ...
      
    // Route::get('/projects', 'ProjectsController@index');
      
    Route::resource('projects''ProjectsController');
      ...
      
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan route:list +--------+-----------+-------------------------+------------------+-------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+-------------------------+------------------+-------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/user | | Closure | api,auth:api | | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | web | | | POST | projects | projects.store | App\Http\Controllers\ProjectsController@store | web | | | GET|HEAD | projects/create | projects.create | App\Http\Controllers\ProjectsController@create | web | | | GET|HEAD | projects/{project} | projects.show | App\Http\Controllers\ProjectsController@show | web | | | PUT|PATCH | projects/{project} | projects.update | App\Http\Controllers\ProjectsController@update | web | | | DELETE | projects/{project} | projects.destroy | App\Http\Controllers\ProjectsController@destroy | web | | | GET|HEAD | projects/{project}/edit | projects.edit | App\Http\Controllers\ProjectsController@edit | web | +--------+-----------+-------------------------+------------------+-------------------------------------------------+--------------+ vagrant@homestead:~/projects/scratch$

    Если при создании контроллера воспользоваться ключами, то можно создать одной командой контроллер с предварительно объявленными методами и модель для него. Удаляем созданные ранее ProjectsController с моделью Project и создаём их заново:

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:controller ProjectsController -r -m Project A App\Project model does not exist. Do you want to generate it? (yes/no) [yes]: > Model created successfully. Controller created successfully. vagrant@homestead:~/projects/scratch$

    Содержимое созданных файлов:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    App\Project;
      use 
    Illuminate\Http\Request;

      class 
    ProjectsController extends Controller
      
    {
          
    /**
           * Display a listing of the resource.
           *
           * @return \Illuminate\Http\Response
           */
          
    public function index()
          {
              
    //
          
    }

          
    /**
           * Show the form for creating a new resource.
           *
           * @return \Illuminate\Http\Response
           */
          
    public function create()
          {
              
    //
          
    }

          
    /**
           * Store a newly created resource in storage.
           *
           * @param  \Illuminate\Http\Request  $request
           * @return \Illuminate\Http\Response
           */
          
    public function store(Request $request)
          {
              
    //
          
    }

          
    /**
           * Display the specified resource.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function show(Project $project)
          {
              
    //
          
    }

          
    /**
           * Show the form for editing the specified resource.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function edit(Project $project)
          {
              
    //
          
    }

          
    /**
           * Update the specified resource in storage.
           *
           * @param  \Illuminate\Http\Request  $request
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function update(Request $requestProject $project)
          {
              
    //
          
    }

          
    /**
           * Remove the specified resource from storage.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function destroy(Project $project)
          {
              
    //
          
    }
      }

      
    app/Project.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Project extends Model
      
    {
          
    //
      
    }
      

    13 эпизод. Опишем методы в классе ProjectsController:

    app/Http/Controllers/ProjectsController.php:
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    App\Project;
      use 
    Illuminate\Http\Request;

      class 
    ProjectsController extends Controller
      
    {
          
    /**
           * Display a listing of the resource.
           *
           * @return \Illuminate\Http\Response
           */
          
    public function index()
          {
              
    $projects Project::all();

              return 
    view('projects.index'compact('projects'));
          }

          
    /**
           * Show the form for creating a new resource.
           *
           * @return \Illuminate\Http\Response
           */
          
    public function create()
          {
              return 
    view('projects.create');
          }

          
    /**
           * Store a newly created resource in storage.
           *
           * @param  \Illuminate\Http\Request  $request
           * @return \Illuminate\Http\Response
           */
          
    public function store(Request $request)
          {
              
    $project = new Project;

              
    $project->title request('title');
              
    $project->description request('description');

              
    $project->save();

              return 
    redirect('/projects');
          }

          
    /**
           * Display the specified resource.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function show(Project $project)
          {
              
    //
          
    }

          
    /**
           * Show the form for editing the specified resource.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function edit(Project $project)
          {
              
    // dd($project);
              // $project = Project::findOrFail($id);

              
    return view('projects.edit'compact('project'));
          }

          
    /**
           * Update the specified resource in storage.
           *
           * @param  \Illuminate\Http\Request  $request
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function update(Request $requestProject $project)
          {
              
    $project->title request('title');
              
    $project->description request('description');
              
    $project->save();
              return 
    redirect('/projects');
          }

          
    /**
           * Remove the specified resource from storage.
           *
           * @param  \App\Project  $project
           * @return \Illuminate\Http\Response
           */
          
    public function destroy(Project $project)
          {
              
    $project->delete();
              return 
    redirect('/projects');
          }
      }

      

    В директории resources/views создаем resources/views/layout.blade.php и поддиректорию resources/views/projects, в которой описываем подключаемые файлы. Привожу окончательный вариант. P.S.: bulma.css - гавно.

    resources/views/layout.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

      
    <!DOCTYPE html>
      <
    html lang="en">
      <
    head>
        <
    meta charset="UTF-8">
        <
    meta name="viewport" content="width=device-width, initial-scale=1.0">
        <
    meta http-equiv="X-UA-Compatible" content="ie=edge">
        <
    title>Document</title>

        <
    link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">
        <
    script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
      </
    head>
      <
    body>
        <
    div class='container'>
          @yield(
    'content')
        </
    div>
      </
    body>
      </
    html>
      
    resources/views/projects/index.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>List projects (index.blade.php)</h1>

        <
    ul>
        @foreach(
    $projects as $project)
          <
    li><a href='/projects/{{ $project->id }}'>{{ $project->title }}</a></li>
        @endforeach
        </
    ul>

        <
    a href='/projects/create'>
          <
    button class="button is-info" form="update" style='margin:2em 0;'>Create new Project</button>
        </
    a>

      @
    endsection
      
    resources/views/projects/create.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>Create Project (create.blade.php)</h1>


        <
    form action='/projects' method='POST' id='update' style='margin:2em 0;'>
          {{ 
    csrf_field() }}


          <
    div class="field">
            <
    label class="label">title</label>
            <
    class="control">
              <
    input class="input" type="text" name='title' placeholder='Enter the title project' value='' required>
            </
    p>
          </
    div>


          <
    div class="field">
            <
    label class="label">Description</label>
            <
    class="control">
              <
    textarea class="textarea" name='description' placeholder='Enter the description project' required></textarea>
            </
    p>
          </
    div>

        </
    form>


        <
    div class="field is-grouped">
          <
    class="control">
            <
    button class="button is-success" form="update">Create Project</button>
          </
    p>
          <
    class="control">
            <
    button class="button is-info" form="update">Cancel</button>
          </
    p>
        </
    div>


      @
    endsection

      
    resources/views/projects/show.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>Show Project "{{ $project->title }} (show.blade.php)"</h1>

        <
    div class='content'>
          {{ 
    $project->description }}
        </
    div>

        <
    a href='/projects/{{ $project->id }}/edit'>
          <
    button class="button is-success" form="update">Update Project</button>
        </
    a>

        <
    a href='/projects'>
          <
    button class="button is-info" form="update">back to list</button>
        </
    a>

      @
    endsection
      
    resources/views/projects/edit.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>Edit Project (edit.blade.php)</h1>


        <
    form action='/projects/{{ $project->id }}' method='POST' id='update' style='margin:2em 0;'>
          {{ 
    csrf_field() }}
          {{ 
    method_field('PATCH') }}


          <
    div class="field">
            <
    label class="label">title</label>
            <
    class="control">
              <
    input class="input" type="text" name='title' value='{{ $project->title }}' required>
            </
    p>
          </
    div>


          <
    div class="field">
            <
    label class="label">Description</label>
            <
    class="control">
              <
    textarea class="textarea" name='description' required>{{ $project->description }}</textarea>
            </
    p>
          </
    div>

        </
    form>


        <
    form action='/projects/{{ $project->id }}' method='POST' id='delete'>
          @
    csrf
          
    @method('DELETE')
        </
    form>


        <
    div class="field is-grouped">
          <
    class="control">
            <
    button class="button is-success" form="update">Update Project</button>
          </
    p>
          <
    class="control">
            <
    button class="button is-danger" form="delete">Delete Project</button>
          </
    p>
          <
    class="control">
            <
    button class="button is-info" form="update">Cancel</button>
          </
    p>
        </
    div>


      @
    endsection
      

    14 эпизод.

    Очистка метода edit()

    Как я понял, в коде выше в описании метода edit() уже чист. Ниже приведу пример нечистого, где аргументом передаётся id проекта и для получения экземпляра объекта необходимо вызвать findOrFail() (закомментированый участок кода) и эквивалентного ему чистого, где экземпляр объекта передаётся в качестве аргумента метода:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

        
    ...
          
    /**
           * Store a newly created resource in storage.
           *
           * @param  \Illuminate\Http\Request  $request
           * @return \Illuminate\Http\Response
           */
          // public function edit($id)
          // {
          //     $project = Project::findOrFail($id);
          //
          //     return view('projects.edit', compact('project'));
          // }
          
    public function edit(Request $request)
          {
              return 
    view('projects.edit'compact('project'));
          }
        ...
      

    Очистка метода store()

    Также можно описать более чисто метод store(), но, в случае вызова create() со всеми пришедшими из формы данными необходимо обезопасить приложение, указав в модели перезаписываемые поля:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

        
    ...
        
    /**
       * Store a newly created resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return \Illuminate\Http\Response
       */
      
    public function store(Request $request)
      {

        
    // Project::create([
        //   'title' => request('title'),
        //   'description' => request('description'),
        // ]);
            
    Project::create(request()->all());

        return 
    redirect('/projects');
      }
        ...
      
    app/Project.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Project extends Model
      
    {
        protected 
    $fillable = [
          
    'title''description',
        ];
      }
      

    либо передав в request() в качестве аргумента массив с именами разрешенных к изменению полей (scratch остановился на этом варианте):

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16

        
    ...
        
    /**
       * Store a newly created resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return \Illuminate\Http\Response
       */
      
    public function store(Request $request)
      {
            
    Project::create(request(['title''description',]));

        return 
    redirect('/projects');
      }
        ...
      
    app/Project.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Project extends Model
      
    {
        protected 
    $guarded = [];
      }
      

    Очистка метода update()

    Аналогично поступим с методом update():

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

        
    ...
        
    /**
       * Update the specified resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @param  \App\Project  $project
       * @return \Illuminate\Http\Response
       */
      
    public function update(Request $requestProject $project)
      {
        
    // $project->title = request('title');
        // $project->description = request('description');
        // $project->save();

        
    $project->update(request(['title''description',]));

        return 
    redirect('/projects');
      }
        ...
      

    Очищенный контроллер

    Содержимое очищенного контроллера:

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    App\Project;
      use 
    Illuminate\Http\Request;

      class 
    ProjectsController extends Controller
      
    {
        
    /**
         * Display a listing of the resource.
         *
         * @return \Illuminate\Http\Response
         */
        
    public function index()
        {
          
    $projects Project::all();

          return 
    view('projects.index'compact('projects'));
        }

        
    /**
         * Show the form for creating a new resource.
         *
         * @return \Illuminate\Http\Response
         */
        
    public function create()
        {
          return 
    view('projects.create');
        }

        
    /**
         * Store a newly created resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return \Illuminate\Http\Response
         */
        
    public function store(Request $request)
        {
          
    Project::create(request(['title''description',]));
          return 
    redirect('/projects');
        }

        
    /**
         * Display the specified resource.
         *
         * @param  \App\Project  $project
         * @return \Illuminate\Http\Response
         */
        
    public function show(Project $project)
        {
          return 
    view('projects.show'compact('project'));
        }

        
    /**
         * Show the form for editing the specified resource.
         *
         * @param  \App\Project  $project
         * @return \Illuminate\Http\Response
         */
        
    public function edit(Project $project)
        {
          return 
    view('projects.edit'compact('project'));
        }

        
    /**
         * Update the specified resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \App\Project  $project
         * @return \Illuminate\Http\Response
         */
        
    public function update(Request $requestProject $project)
        {
          
    $project->update(request(['title''description',]));
          return 
    redirect('/projects');
        }

        
    /**
         * Remove the specified resource from storage.
         *
         * @param  \App\Project  $project
         * @return \Illuminate\Http\Response
         */
        
    public function destroy(Project $project)
        {
          
    $project->delete();
          return 
    redirect('/projects');
        }
      }
      

    15 эпизод.

    В уровне отображения для защиты от отправки пустых значений используется дополнительный атрибут required, но от особо умных пользователей необходима защита и на стороне контроллера. Она реализуется с помощью метода validate().

    app/Http/Controllers/ProjectsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

      
    ...
      
    /**
       * Store a newly created resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return \Illuminate\Http\Response
       */
      
    public function store(Request $request)
      {
          
    request()->validate([
              
    'title' => 'require',
              
    'description' => 'require',
          ]);
          
    Project::create(request(['title''description',]));
          return 
    redirect('/projects');
      }
      ...
      

    При срабатывании метода validate() контроллер возвращает редирект на $_SERVER[referer] с передачей в качестве аргумента масива $errors (?). Опишем d файле create.blade.php элемент, в котором будут выводиться ошибки:

    resources/views/projects/create.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>Create Project (create.blade.php)</h1>


        <
    form action='/projects' method='POST' id='update' style='margin:2em 0;'>
          {{ 
    csrf_field() }}


          <
    div class="field">
            <
    label class="label">title</label>
            <
    class="control">
              <
    input
                
    class="input{{ $errors->has('title') ? ' is-danger':'' }}"
                
    type="text"
                
    name='title'
                
    placeholder='Enter the title project'
                
    value='{{ old('title') }}'>
            </
    p>
          </
    div>


          <
    div class="field">
            <
    label class="label">Description</label>
            <
    class="control">
              <
    textarea
                
    class="textarea{{ $errors->has('description') ? ' is-danger':'' }}"
                
    name='description'
                
    placeholder='Enter the description project'>{{ old('description') }}</textarea>
            </
    p>
          </
    div>

        </
    form>


        <
    div class="field is-grouped">
          <
    class="control">
            <
    button class="button is-success" form="update">Create Project</button>
          </
    p>
          <
    class="control">
            <
    button class="button is-info" form="update">Cancel</button>
          </
    p>
        </
    div>


        @if(
    $errors->any())
        <
    div class='notification is-danger'>
          <
    ul>
            @foreach(
    $errors->all() as $error)
              <
    li>{{ $error }}</li>
            @endforeach
          </
    ul>
        </
    div>
        @endif

      @
    endsection
      

    Временно удалим аттрибут required у элемента textarea и проверим корректность срабатывания validate(), отправив на сервер пустое значение description:

    Рис. 4 корректность срабатывания метода validate()
    Рис. 4 корректность срабатывания метода validate()

    Полный список правил валидации представлен в официальной документации.

    16 эпизод.

    Воспользуемся ключами и создадим модель Task вместе с фабрикой и файлом миграции:

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:model Task -m -f Model created successfully. Factory created successfully. Created Migration: 2019_02_17_202511_create_tasks_table vagrant@homestead:~/projects/scratch$

    Опишем и выполним миграцию:

    /home/yo/projects/scratch/database/migrations/2019_02_17_202511_create_tasks_table.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

      
    <?php

      
    use Illuminate\Support\Facades\Schema;
      use 
    Illuminate\Database\Schema\Blueprint;
      use 
    Illuminate\Database\Migrations\Migration;

      class 
    CreateTasksTable extends Migration
      
    {
        
    /**
         * Run the migrations.
         *
         * @return void
         */
        
    public function up()
        {
          
    Schema::create('tasks', function (Blueprint $table) {
            
    $table->increments('id');
            
    $table->unsignedInteger('project_id');
            
    $table->text('description');
            
    $table->boolean('completed')->default(false);
            
    $table->timestamps();
          });
        }

        
    /**
         * Reverse the migrations.
         *
         * @return void
         */
        
    public function down()
        {
          
    Schema::dropIfExists('tasks');
        }
      }
      
    bash:
    vagrant@homestead:~/projects/scratch$ php artisan migrate Migrating: 2019_02_17_202511_create_tasks_table Migrated: 2019_02_17_202511_create_tasks_table vagrant@homestead:~/projects/scratch$

    Опишем отношение в модели Project:

    /home/yo/projects/scratch/app/Project.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Project extends Model
      
    {
        
    // protected $fillable = [
        //   'title', 'description',
        // ];
        
    protected $guarded = [];

        public function 
    tasks()
        {
          return 
    $this->hasMany(Task::class);
        }
      }
      

    Создадим запись в таблице tasks с помощью tinker и вызовем описанный в модели Project метод tasks():

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> App\Project::first(); => App\Project {#2918 id: 1, title: "First Project", description: "Description for First Project.", created_at: "2019-02-16 19:56:39", updated_at: "2019-02-17 14:53:53", } >>> // вызовем описанный выше метод task (почему метод без круглых скобок???): >>> App\Project::first()->tasks; => Illuminate\Database\Eloquent\Collection {#2912 all: [], } >>> // tinker вернул пустую коллекцию >>> // добавим запись в таблицу tasks: >>> $task = new App\Task; => App\Task {#2918} >>> $task->project_id = 2; => 2 >>> $task->description = 'description for task project with id=2'; => "description for task project with id=2" >>> $task->save(); => true >>> >>> // попробуем еще раз: >>> App\Project::first()->tasks; => Illuminate\Database\Eloquent\Collection {#2911 all: [], } >>> >>> // странно.. >>> App\Task::all(); => Illuminate\Database\Eloquent\Collection {#2912 all: [ App\Task {#2921 id: 1, project_id: 2, description: "description for task project with id=2", completed: 0, created_at: "2019-02-17 20:53:48", updated_at: "2019-02-17 20:53:48", }, ], } >>> // Семён Семёныч! мы же первый экземпляр вызываем! >>> $task = new App\Task; => App\Task {#2917} >>> $task->project_id = 1; => 1 >>> $task->save(); Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field 'description' doesn't have a default value (SQL: insert into `tasks` (`project_id`, `updated_at`, `created_at`) values (1, 2019-02-17 21:39:46, 2019-02-17 21:39:46))' >>> $task->description = 'description for task project with id=1'; => "description for task project with id=1" >>> $task->save(); => true >>> >>> // контрольный >>> App\Project::first()->tasks; => Illuminate\Database\Eloquent\Collection {#2926 all: [ App\Task {#2914 id: 2, project_id: 1, description: "description for task project with id=1", completed: 0, created_at: "2019-02-17 21:39:46", updated_at: "2019-02-17 21:39:46", }, ], } >>> >>> >>> >>> // создадим еще одну задачу для project_id = 1 >>> $task = new App\Task; => App\Task {#2912} >>> $task->description = 'description for another task project with id=1'; => "description for another task project with id=1" >>> $task->project_id = 1; => 1 >>> $task->save(); => true >>> // просмотрим возвращаемое значение: >>> App\Project::first()->tasks; => Illuminate\Database\Eloquent\Collection {#2920 all: [ App\Task {#2915 id: 2, project_id: 1, description: "description for task project with id=1", completed: 0, created_at: "2019-02-17 21:39:46", updated_at: "2019-02-17 21:39:46", }, App\Task {#2924 id: 3, project_id: 1, description: "description for another task project with id=1", completed: 0, created_at: "2019-02-17 22:34:12", updated_at: "2019-02-17 22:34:12", }, ], } >>>

    Отобразим поставленные задачи:

    views/projects/show.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13

      
    ...
      @if(
    $project->tasks->count())
          <
    div class='content'>
              @foreach(
    $project->tasks as $task)
                  <
    ul>
                      <
    li>{{ $task->description }}</li>
                  </
    ul>
              @endforeach
          </
    div>
      @endif
      ...
      
    Рис. 5 Отображение поставленных задач
    Рис. 5 Отображение поставленных задач

    Опишем обратное отношение в модели Task:

    app/Task.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Task extends Model
      
    {
        public function 
    project()
        {
          return 
    $this->belongsTo(Project::class);
        }
      }
      

    Ахтунг! Tinker увидел его только после перезагрузки:

    bash:
    >>> App\Task::first()->projects; => null >>> App\Task::first(); => App\Task {#2922 id: 1, project_id: 2, description: "description for task project with id=2", completed: 0, created_at: "2019-02-17 20:53:48", updated_at: "2019-02-17 20:53:48", } >>> exit Exit: Goodbye vagrant@homestead:~/projects/scratch$ vagrant@homestead:~/projects/scratch$ php artisan tinker Psy Shell v0.9.9 (PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman >>> // можно вызвать непосредственно через (модель?) App\Task: >>> App\Task::first()->project; => App\Project {#2919 id: 2, title: "Second Project updated2", description: "Description for Second Project.", created_at: "2019-02-16 19:58:50", updated_at: "2019-02-17 14:54:04", } >>> // или через $task: >>> $task = App\Task::first(); => App\Task {#2923 id: 1, project_id: 2, description: "description for task project with id=2", completed: 0, created_at: "2019-02-17 20:53:48", updated_at: "2019-02-17 20:53:48", } >>> $task->project; => App\Project {#2922 id: 2, title: "Second Project updated2", description: "Description for Second Project.", created_at: "2019-02-16 19:58:50", updated_at: "2019-02-17 14:54:04", } >>>

    17 эпизод. Создаём контроллер ProjectTasksController.

    bash:
    vagrant@homestead:~/projects/scratch$ php artisan make:controller ProjectTasksController -r Controller created successfully. vagrant@homestead:~/projects/scratch$

    Так как при создании файла был использован ключ '-r', в описании класса уже присутствуют заготовки стандартных методов. В методе 'update' будет производиться перезапись объекта класса Task.

    Для того, чтобы принять в метод не id (по-умолчанию), а сразу необходимый объект (экземпляр класса Task), необходимо импортировать (в пространство имен?) модель Task и изменить аргументы метода следующим образом:

    app/Http/Controllers/ProjectTasksController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

      
    <?php

      
    namespace App\Http\Controllers;

      use 
    Illuminate\Http\Request;
      use 
    App\Task;  // 1

      
    class ProjectTasksController extends Controller
      
    {

        ...

        
    /**
         * Update the specified resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        //public function update(Request $request, $id)
        
    public function update(Task $task// 2
        
    {
          
    // dd($task);  // Task {#222 ▶}

          // dd(request()->all());   // array:3 [▼
          //             //  "_method" => "PATCH"
          //             //  "_token" => "gOQD6zRrSpXwbDYK6ce7jy340WCDXDR7aV17t9Yj"
          //             //  "completed" => "on"
          //             // ]

          
    $task->update([
            
    'completed' => request()->has('completed'),
          ]);

          return 
    back(); // or 'return redirect('/projects');'
        
    }

        ...
      }
      

    Добавим поле 'completed' в свойство $fillable для снятия его защиты от массового заполнения:

    app/Task.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18

      
    <?php

      
    namespace App;

      use 
    Illuminate\Database\Eloquent\Model;

      class 
    Task extends Model
      
    {

        public 
    $fillable = ['completed'];

        public function 
    project()
        {
          return 
    $this->belongsTo(Project::class);
        }
      }
      

    Добавим роут:

    app/Task.php:
    1
    2
    3
    4

      
    ...
      
    Route::patch('task/{task}''ProjectTasksController@update');
      

    Улучшим отображение, воспользовавшись так внезапно открывшимися возможностями:

    resources/views/projects/show.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47

      
    @extends('layout')

      @
    section('content')
        <
    h1 class='title' style='margin:2em 0;'>Show Project "{{ $project->title }} (show.blade.php)"</h1>

        <
    div class='content'>
          {{ 
    $project->description }}
        </
    div>

        @if(
    $project->tasks->count())
          <!--
    div class='content'>
            @foreach(
    $project->tasks as $task)
              <
    ul>
                <
    li>{{ $task->description }}</li>
              </
    ul>
            @endforeach
          </
    div-->
          <
    div class='content'>
            @foreach(
    $project->tasks as $task)
              <
    form method='POST' action='/tasks/{{ $task->id }}'>
                @
    method('PATCH')
                @
    csrf
                
    <label class='checkbox' for='completed'{!!
                    
    $task->completed "style='text-decoration:line-through;'" ''
                  
    !!}>
                  <
    input
                    type
    ='checkbox'
                    
    name='completed'
                    
    onChange='this.form.submit()'{{ $task->completed 'checked' ''}}>
                    {{ 
    $task->description }}
                </
    label>
              </
    form>
            @endforeach
          </
    div>
        @endif

        <
    a href='/projects/{{ $project->id }}/edit'>
          <
    button class="button is-success" form="update">Update Project</button>
        </
    a>

        <
    a href='/projects'>
          <
    button class="button is-info" form="update">back to list</button>
        </
    a>

      @
    endsection
      
    Рис. 6 Отображение поставленных задач 2
    Рис. 6 Отображение поставленных задач 2

    18 эпизод.

    Добавим роут post для контроллера ProjectTasksController:

    /home/yo/projects/scratch/routes/web.php:
    1
    2
    3
    4
    5

      
    ...
      
    Route::patch('/tasks/{task}''ProjectTasksController@update');
      
    Route::post('/projects/{project}/tasks''ProjectTasksController@store');
      

    Отредактируем метод store контроллера ProjectTasksController (пример из видео не сработал. разобраться почему. Выдаёт ошибку: SQLSTATE[HY000]: General error: 1364 Field 'project_id' doesn't have a default value (SQL: insert into `tasks` (`updated_at`, `created_at`) values (2019-02-26 00:09:11, 2019-02-26 00:09:11))):

    app/Http/Controllers/ProjectTasksController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

      
    ...
        
    /**
         * Store a newly created resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return \Illuminate\Http\Response
         */
        // public function store(Request $request)
        
    public function store(Project $project)
        {
          
    // dd(request('description'));
          // dd($project->id);

          // почему не работает пример из видео??? (https://laracasts.com/series/laravel-from-scratch-2018/episodes/18 03:20)
          // Task::create([
          //   'project_id' => $project->id,
          //   'description' => request('description'),
          // ]);

          
    $task = new Task;
          
    $task->project_id $project->id;
          
    $task->description request('description');
          
    $task->save();

          return 
    back();
        }
      ...
      

    Отредактируем вид, добавив форму для добавления задач проекта:

    patchToFile:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

      
    <div class='content'>{{-- add new task --}}
          <
    form method='POST' action='/projects/{{ $project->id }}/tasks' class='box'>

              <
    div class='field'>

                  @
    csrf

                  
    <label class='label' for='description'>New Task:</label>
                  <
    div class='control'>
                      <
    input type='text' class='input' name='description'>
                  </
    div>
              </
    div>

              <
    div class='field'>
                  <
    div class='control'>
                      <
    button type='submit' class='button is-link'>add Task</button>
                  </
    div>
              </
    div>

          </
    form>
      </
    div>{{-- /add new task --}}
      
    code
    bash:
    code
    bash:
    code
    bash:
    code
    bash:
    code
    bash:
    code
    bash:
    code
    bash:
    code
    bash: