Что такое trait laravel
Простая Мультитенантность в Laravel через Trait
Мультитенантность( она же Мультиарендность, она же Multi-Tenancy) распространенное явление в веб-проектах — когда вы предоставляете доступ к записям только тем пользователям, кто их создал. Другими словами, каждый управляет своими собственными данными и не видит чужие. Эта статья покажет вам, как реализовать это в одной базе данных самым простым способом.
Внимание: есть несколько более сложных и гибких реализаций мультитенантности, включая использование различных пакетов, но в этой статье я расскажу о самом быстром способе.
Что будем создавать
Представьте себе сайт по управлению Книгами и каждый пользователь может видеть только свои загруженные книги. Каждая книга привязана к стране происхождения из модели Country и эта модель должна быть доступна каждому, без мультитенантности.
Таким образом, мы будем имитировать более сложные примеры из реальной жизни, где часть моделей мультитенантны, а часть — публичны.
Шаг 1. Вход Пользователя, создавшего запись
Для всех моделей, которые должны быть мультитенантны, нам нужно создать дополнительное поле в их таблице базы данных.
и добавляем в app/Book.php — смотри последнее значение:
Как мы теперь можем заполнить это поле автоматически? Конечно можно это сделать через Наблюдателя (Observer), но, для этого примера, давайте создадим Trait, который будем использовать как для сохранения записи, так и для ее фильтрации.
Создаем новую папку app/Traits и файл app/Traits/Multitenantable.php и вставляем туда:
Обратите внимание на имя метода bootMultitenantable() — соглашение об именах bootXYZ() означает, что Laravel автоматически запустит этот метод при использовании Trait’а. Можно назвать это «конструктором» Trait’а.
Итак, что мы здесь делаем? Если пользователь залогинен, мы добавляем его идентификатор в поле created_by_user_id, какая бы ни была модель.
Видите, мы нигде здесь не упоминаем Book или какую-либо другую модель. Таким образом, мы можем добавить этот Trait к любым моделям, которые захотим.
Но давайте начнем с Book. Добавить Trait очень просто — всего пара строк в app/Book.php:
И всё, поле create_by_user_id будет заполнено автоматически при вызове Book::create().
Шаг 2. Фильтрация данных по пользователю
Следующим шагом будет фильтрация данных, когда кто-либо получает доступ к списку книг или пытается получить книгу по ее идентификатору.
Нам нужно добавить глобальный Scope для всех запросов к этой модели.
И это тоже можно гибко реализовать в этом же самом app/Traits/Multitenantable.php. Вот его полный код:
Мы подключили класс Eloquent/Builder, а затем использовали static::addGlobalScope() для фильтрации любого запроса с залогиненным пользователем.
Вот и все, теперь каждый пользователь увидит только свои книги.
В дополнение к этому, фильтр будет работать каждый раз, когда кто-либо получает список книг, где бы он ни находился в проекте. Например, если в будущем book_id будет полем для некоторых других таблиц, таких как chapters — они по прежнему смогут выбирать их только из своих книг.
Шаг 3. Добавление Trait к другим моделям
Помните, нашей задачей было оставить модель Countries доступной для всех пользователей? А решение этого в том, что мы просто не используем наш Trait в модели app/Country.php.
Короче говоря, правило простое: для моделей, которые должны быть «мультитенантными» используйте этот Trait. Вот и все.
Шаг 4. Как насчет администратора, который может просмотреть все записи?
Очевидно, что какой-то «суперпользователь» системы должен видеть все книги, верно?
Этот функционал теперь зависит от вашей реализации системы пользователь-роль-права, но, в общих чертах, я расскажу, как вы можете добавить эти права.
В фильтре Trait нужно добавить:
Да, всё просто — проверяйте через оператор if, кто из пользователей может обойти этот фильтр.
Наш Телеграм-канал — следите за новостями о Laravel.
Задать вопросы по урокам можно на нашем форуме.
Используем Трейты для Laravel Eloquent связей
Автор: Виктор Сочельников (Копирайтер) | 2018-09-09 07:17:00
Допустим, у нас есть Post модель, которая выглядит примерно так:
У нас есть некоторые методы, определенные для свойств Post модели, и некоторые для связи с моделью Account.
Теперь, когда мы добавим другую модель, которая имеет отношение к учетной записи, нам придется добавить те же методы. Это может занять много времени, и если вы когда-нибудь хотели изменить методы, вам придется делать это на всех моделях, которые имеют эти отношения.
Traits (Трейты)
Я начну с Php.net документации:
Trait похож на класс, но предназначен только для группирования функциональных возможностей в мелкозернистой и согласованным способом. Невозможно создать экземпляр трейта самостоятельно. Он является дополнением к традиционному наследованию и обеспечивает горизонтальную композицию поведения, то есть применение членов класса без наследования.
Поэтому в нашем случае мы будем использовать трейты. Мы создадим характеристику HasAccountTrait, которая будет содержать все методы для связи, принадлежащей учетной записи:
Затем мы изменим нашу Post модель, чтобы использовать наш трейт, она будет выглядеть таким образом:
Вывод:
Использование трейтов означает, что мы можем создать чистый код при определении связей с одним и тем же источником. Это также может ускорить разработку в зависимости от количества связей в приложении.
Используем трейты для полиморфных связей в Laravel
Введение
Поговорим о возможном применении трейтов вместе с полиморфными отношениями в Laravel.
Содержание статьи:
Описание предметной области
Мы будем разрабатывать систему, в которой некие сотрудники и некие команды могут быть прикреплены к проекту. Сущностями предметной области будут сотрудники, команды и проекты: команда состоит из сотрудников, на проект могут быть прикреплены сотрудники и команды. Между командой и сотрудником отношение many-to-many (допустим, что сотрудник может участвовать в разных командах), many-to-many между проектами и сотрудниками, many-to-many между командами и проектами. Для дальнейшего рассмотрения опустим реализацию связи между командой и сотрудниками, сосредоточимся на отношении команд и сотрудников к проекту.
Создание приложения
Приложения на Laravel очень просто создавать, используя пакет-создатель приложений. После его установки создание нового приложения умещается в одну команду:
Возможные структуры БД
Если идти нормализованным путем, то нам понадобится три таблицы для сущностей и ещё три таблицы для связей: сотрудники-команды, сотрудники-проекты, команды-проекты.
Если снизить уровень нормализации, то можно объединить таблицы для связей сотрудник-проект и команда-проект в одну, разделяя тип связи по дополнительному полю с типом (допустим, 1 — сотрудник, 2 — команда).
Идея морф-связей похожа на менее нормализованный вариант, только вместо дополнительного поля с типом используется два — один для имени класса модели, второй для её идентификатора.
Создание сущностей
Нам понадобятся модели, миграции и фабрики для сотрудников, команд, проектов и прикреплений. Команды для создания всего этого:
После выполнения команды мы получим файлы моделей в App/, файлы миграций в папке database/migrations/ и фабрики в database/factories/.
Перейдём к написанию миграций. Во всех сущностях может быть много полей, но мы возьмем по минимуму: у сотрудника, команды и проекта будет только имя. Позволю себе сократить список миграций до двух — для сущностей и для полиморфного отношения.
Миграция для сущностей
Для полиморфного отношения
Обратите внимание, что для создания полей полиморфного отношения нужно указать функцию morphs().
Теперь к моделям
Модель команды идентична модели сотрудника:
Модель проекта
Прикрепление
Фабрики идентичны для всех сущностей
Сущности готовы, переходим к трейту.
Использование трейта
Полиморфные отношения в Laravel подразумевают разные типы отношений для главных и прикрепляемых моделей — в проекте указывается тип связи morphedByMany(), а в сущностях — morphToMany(). Для всех прикрепляемых моделей метод для описания связи будет одинаков, поэтому логично вынести этот метод в трейт и использовать его в модели сотрудника и команды.
Создадим новую директорию app/Traits и трейт с названием полиморфного отношения: Attachable.php
Осталось добавить этот трейт в модели сотрудника и команды через use.
Переходим к проверке работоспособности с помощью тестов.
Написание тестов
По стандарту, Laravel использует PHPUnit для тестирования. Создать тесты для связи:
Файл теста можно найти в tests/Feature/. Для обновления состояния БД перед запуском тестов будем использовать трейт RefreshDatabase.
Проверим работу морфа со стороны проекта и трейта со стороны команды и сотрудников
Трейты позволяют не дублировать общие методы для полиморфных отношений внутри классов моделей, также их можно использовать, если у вас есть одинаковые поля во многих таблицах (например, автор записи) — тут тоже можно сделать трейт с методом связи.
Буду рад слышать ваши кейсы применения трейтов в Laravel и PHP.
Что такое trait laravel
Traits are a handy way of reusing code in PHP, another way to help keep your code DRY (or “don’t repeat yourself”). If you’ve used whats commonly referred to as Mixins in some languages and frameworks, you’ll be probably be familiar with Traits. It’s great that we can write some code, reuse that again and again throughout our codebase more efficiently, meanwhile – It’s good practice and helps us maintain the codebase easier, so yay for Laravel Traits / PHP Traits!
How using Traits help us in Laravel
In Laravel, you may of come across a variety of “out the box” traits that you can use, one of the most popular ones you may see across your Models is the Soft Deletes Trait. In short – using this trait allows you to “delete” your model, but without removing it entirely from the database. Utilizing a column in the database called “deleted_at” to “soft” delete the model when the trait is used. This can be useful if you frequently delete and restore models.
As you can see, using traits in your projects can be an easy way to create and add impressive reusable functionality to your project.
How to avoid abusing Traits
As traits allow you to easily add another set of functions to your classes, they may unfortunately become the place to be when you just want to add another little bit of shared functionality here and there if your not careful.
This is not a problem with Traits themselves, but more so how they are used. In your project, be careful to not let them get bloated with functionality that doesn’t concern them. Don’t add unnecessary code to a trait that doesn’t need to be there or can be abstracted away into another Class or Trait. Where possible, try and stick to the Single Responsibility Principle.
An example Image/File upload Laravel Trait
Why do we need this Trait
Majority of Laravel applications have a form somewhere with an image or file upload field in it, perhaps in an admin panel, or a form visible to the public, like changing your profile picture. So this becomes a great example for creating your first Trait.
Our new Trait would be responsible for getting the file upload from the request, checking if it’s valid, storing it and then returning the newly stored file path. It’s a small but handy trait to have – without using it, how many times would you have to rewrite the same code to handle this file upload functionality – in the create form? the edit form? and then for multiple occurances of a file upload in your forms? It can easily mount up.
Creating our Trait
Creating the Trait is easy! I chose to store all my traits in folder called Traits (usually located at App/Traits), using the namespace App\Traits, this way all my traits are easy to find. You can organise them in subfolders too.
So let’s go ahead and create the basic structure of our Trait. It will have a function called verifyAndStoreImage so that when our Trait is used, it’s clear what this function is expected to do.
There’s not much code required to create a Trait, so it’s really easy to begin creating more Traits for your project where applicable.
We will fill out the rest of our trait, to include some basic file validation, storing the file locally, and then returning the file upload path.
To use your newly created Trait in your code, is really simple too. You’ll have to import the Trait, but after that you can use it like any other function, you can even pass arguments into them too. We will use the StoreImageTrait (that we created above) in our example Post Controller.
Once our image upload trait has been imported, we can then use it in one of our methods, in this case, we want to add it to our Store method in the Post Controller
Laravel Traits, the conclusion
We’ve learnt what a Trait is, and how using them can offer you an invaluable solution not only help you stop repeating the same code over and over again, but improving your code by using them, making it easier to create cleaner, more readable and maintainable code.
Сделайте модели Laravel лучше с помощью трейтов
Я полагаю, что большинство веб-разработчиков сталкивались с проблемой “жирных” моделей в какой-то момент своей карьеры. Даже хорошие разработчики, следующие SOLID принципам, сохраняя правила проверки и другую бизнес-логику вне моделей, иногда сталкиваются с этой проблемой.
Автор блога не разделяет позицию автора статьи.
К счастью, есть глупо простое решение, которое я никогда раньше не видел ни в одном проекте Laravel. Вот почему я решил написать эту короткую статью.
Типичная жирная модель
Начнем с примера “жирной” модели:
Как вы видите, отношения и области действия раздувают код и делают его трудным для чтения.
Трейты спешат на помощь!
Трейты позволяют разработчику использовать наборы методов в нескольких независимых классах.
И это именно то, что мы ищем!
Давайте посмотрим, как будет выглядеть код, если мы заменим отношение “orders”на трейт.
Я предпочитаю помещать отношения в отдельный Relations каталог внутри Models каталога (обратите внимание на пространство имен).
А вот и класс после нашего небольшого рефакторинга:
Разница не очень очевидна, но позвольте мне показать вам тот же самый класс после того как я разделил все релейшены и скоупы на отдельные трейты:
Надеюсь, теперь вы убедились, что это гораздо лучше и легче читать. У нашей бывшей жирной модели есть хороший шанс стать одним из новых Ангелов Victoria’s Secret
Выводы
Пожалуйста, не извлекайте каждое отдельное отношение в отдельный трейт. Если вам нужно расшифровать отношение, которое не следует соглашениям об именовании по умолчанию Laravel, было бы лучше не извлекать его, поскольку оно дает больше информации человеку, который читает код. Но если ваше отношение простое и следует условностям, то перенос его на трейт-хорошая идея. Его можно даже повторно использовать в ситуациях, когда, например, две модели имеют сходные belongsTo отношения.
Несмотря на то, что они доступны начиная с PHP 5.4, трейты не очень широко распространены в сообществе PHP, и эта короткая статья показывает хороший пример преимуществ, которые вы можете получить, используя их в аналогичных сценариях.