Как вставить новые записи в БД Laravel Eloquent

Eloquent – это объектно-реляционный преобразователь (ORM), который включен в структуру Laravel по умолчанию. В этой серии статей вы узнаете, как делать запросы к базе данных и как работать с отношениями в Laravel Eloquent. Также вы сможете попрактиковаться на примерах: мы попробуем улучшить демо-приложение с помощью новых моделей и отношений.

Примечание: Все мануалы данной серии можно найти по тегу Laravel Eloquent.

В предыдущем мануале этой серии вы создали две модели, LinkList и Link, и настроили отношения «один ко многим» между ними. В этом руководстве вы узнаете, как вставлять ссылки и списки в БД с помощью моделей Eloquent. Чтобы немного сократить объем работы, мы применим пользовательские команды Artisan для управления ссылками и списками из командной строки.

Одним из самых важных преимуществ использования ORM-системы является возможность управлять строками в таблице БД как объектами в кодовой базе. В Eloquent, как и в других ORM, объект предоставляет методы, которые можно использовать для сохранения его в базе данных, что избавляет вас от необходимости писать SQL-команды и вручную управлять данными в таблицах.

При работе с отношениями «один ко многим» в Laravel Eloquent есть несколько различных вариантов сохранения связанных моделей. В большинстве случаев мы должны сначала настроить модель, представляющую первую сторону отношения one (в нашем примере этой моделью является LinkList) и сохранить ее в базе данных. После этого вы сможете ссылаться на эту модель (которая после сохранения представляет собой запись в БД) при настройке второй стороны, many (модель Link). Это также означает, что вам понадобится один или несколько списков, чтобы иметь возможность создавать ссылки.

Однако перед созданием новой команды для вставки списков следует обновить существующую команду link:new, так как пока что она не поддерживает функции списка.

Откройте в редакторе кода следующий файл:

app/Console/Commands/LinkNew.php

Вы увидите такой код:

<?php

namespace App\Console\Commands;

use App\Models\Link;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class LinkNew extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:new';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a New Link';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $url = $this->ask('Link URL:');


       if (!filter_var($url, FILTER_VALIDATE_URL)) {
            $this->error("Invalid URL. Exiting...");
            return 1;
        }

        $description = $this->ask('Link Description:');

        $this->info("New Link:");
        $this->info($url . ' - ' . $description);

        if ($this->confirm('Is this information correct?')) {
            $link = new Link();
            $link->url = $url;
            $link->description = $description;
            $link->save();

            $this->info("Saved.");
        }

        return 0;
    }
}

Метод handle() – это то место, где команда выполняет предписанные процедуры. Вот что он делает:

  1. Метод ask(), доступный через родительский класс Illuminate\Console\Command, используется для получения пользовательского ввода в командной строке. Он запросит у пользователя ссылку и подтвердит ввод, чтобы убедиться, что это действительный URL.
  2. Затем сценарий запрашивает описание (оно опционально).
  3. После получения значений url и description скрипт запросит подтверждение с помощью метода confirm(), доступного через родительский Illuminate\Console\Command.
  4. Если подтверждение отправлено с помощью y или yes, сценарий создаст новый объект ссылки и сохранит его в базе данных с помощью метода save(), доступного через родительский класс Illuminate\Database\Eloquent\Model.
  5. Сценарий выводит сообщение, информирующее пользователя о том, что ссылка была сохранена в базе данных, используя метод вывода info.

Примечание: В контексте приложений командной строки, работающих на bash, ненулевые возвращаемые значения нужны, чтобы сигнализировать, что приложение завершилось с ошибкой. А 0 означает, что оно завершилось успешно.

Если вы сейчас запустите команду link:new, она прервется до завершения, потому что БД ожидает, что каждая ссылка будет связана со списком. Вы должны разрешить пользователю выбирать, в какой список должна быть включена ссылка. Если пользователь не укажет список, команда использует список по умолчанию.

Следующий код попросит пользователя указать список или использовать список по умолчанию, оставив поле пустым. Затем он попытается найти указанный список или создать новый (если пользователь назвал список, который еще не существует). Чтобы найти список, предоставленный пользователем, этот код использует метод firstWhere для поиска списка на основе его поля slug. Затем он сохраняет новую ссылку, используя отношение links(), к которому можно получить доступ из объекта LinkList.

Замените текущий код в классе app/Console/Commands/LinkNew.php следующим кодом:

<?php

namespace App\Console\Commands;

use App\Models\Link;
use App\Models\LinkList;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class LinkNew extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:new';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a New Link';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $url = $this->ask('Link URL');

        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            $this->error("Invalid URL. Exiting...");
            return 1;
        }

        $description = $this->ask('Link Description');
        $list_name = $this->ask('Link List (leave blank to use default)') ?? "default";

        $this->info("New Link:");
        $this->info($url . ' - ' . $description);
        $this->info("Listed in: " . $list_name);

        if ($this->confirm('Is this information correct?')) {
            $list = LinkList::firstWhere('slug', $list_name);
            if (!$list) {
                $list = new LinkList();
                $list->title = $list_name;
                $list->slug = $list_name;
                $list->save();
            }

            $link = new Link();
            $link->url = $url;
            $link->description = $description;
            $list->links()->save($link);

            $this->info("Saved.");
        }

        return 0;
    }
}

Сохраните и закройте файл, когда закончите. Затем запустите команду:

docker-compose exec app php artisan link:new

Вам будет предложено ввести URL-адрес, описание и название списка, если вы не хотите сохранять эту ссылку в списке по умолчанию.

Если после сохранения новой ссылки вы запустите команду link:show, вы увидите новую ссылку. Однако в выводе еще нет информации о списках. Теперь нам нужно обновить команду LinkShow, чтобы включить в вывод столбец, в котором отображается эта информация.

Откройте файл app/Console/Commands/LinkShow.php в редакторе кода:

app/Console/Commands/LinkShow.php

Вот как должен выглядеть этот класс:

<?php

namespace App\Console\Commands;

use App\Models\Link;
use Illuminate\Console\Command;

class LinkShow extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:show';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List links saved in the database';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $headers = [ 'id', 'url', 'description' ];
        $links = Link::all(['id', 'url', 'description'])->toArray();
        $this->table($headers, $links);

        return 0;
    }
}

Вы увидите, что текущий метод handle() получает определенное количество полей и преобразует результат в массив. По умолчанию результаты поступают из Eloquent как коллекции, поэтому эта функция преобразует их в массив, чтобы использовать эти данные в методе table(). Проблема в том, что при преобразовании массива между моделями классов (Link и LinkList) теряется отношение, что затрудняет доступ к информации о списке, которому присвоена ссылка.

Сейчас нам нужно изменить этот код, чтобы он извлекал из БД весь объект Link, включая связанные объекты. Чтобы создать массив, который подходит методу table(), мы можем выполнить итерацию по коллекции результатов, возвращаемых Link::all().

Замените текущее содержимое в файле app/Console/Commands/LinkShow.php следующим кодом:

<?php

namespace App\Console\Commands;

use App\Models\Link;
use Illuminate\Console\Command;

class LinkShow extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:show';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List links saved in the database';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $headers = [ 'id', 'url', 'list', 'description' ];
        $links = Link::all();

        $table_rows = [];
        foreach ($links as $link) {
            $table_rows[] = [ $link->id, $link->url, $link->link_list->slug, $link->description ];
        }

        $this->table($headers, $table_rows);

        return 0;
    }
}

Если мы теперь запустим метод link:show, мы увидим дополнительный столбец, показывающий заголовок списка:

+----+-----------------------------------------------------------------------------------------+-----------+--------------------------------------+
| id | url                                                                                     | list      | description                          |
+----+-----------------------------------------------------------------------------------------+-----------+--------------------------------------+
| 1  | https://digitalocean.com                                                                | default   | DigitalOcean Website                 |
| 2  | https://digitalocean.com/community/tutorials                                            | tutorials | DO Tutorials                         |
| 3  | https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04 | tutorials | Initial server setup on ubuntu 20.04 |
+----+-----------------------------------------------------------------------------------------+-----------+--------------------------------------+

Позже в этой серии руководств мы обновим фронт-энд и код основного маршрута, чтобы при отображении ссылки организовывались в списки. Пока что для добавления, переноса и проверки изменений в БД и моделях мы будем использовать командную строку.

В следующем руководстве этой серии мы продемонстрируем другой способ вставки новых записей в БД с помощью моделей Eloquent – на этот раз на основе сидеров.

Tags: , ,

Добавить комментарий