Использование нескольких трейтов
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 52 из 91.
Один трейт полезен, но настоящая мощь проявляется тогда, когда вы объединяете несколько трейтов в одном классе. PHP позволяет подключать столько трейтов, сколько вам нужно, наделяя ваш класс возможностями из нескольких источников одновременно.
Чтобы использовать несколько трейтов, просто перечислите их через запятую после ключевого слова use:
<?php
trait Timestampable {
public function getTimestamp(): string {
return date('Y-m-d H:i:s');
}
}
trait Identifiable {
private string $id;
public function generateId(): void {
$this->id = uniqid('id_');
}
public function getId(): string {
return $this->id;
}
}
class Post {
use Timestampable, Identifiable;
public function __construct(public string $title) {
$this->generateId();
}
}
$post = new Post("Hello World");
echo $post->getId() . " - " . $post->getTimestamp();
Вывод:
id_6789abc123 - 2024-01-15 10:30:00Класс Post теперь имеет методы из обоих трейтов. Каждый трейт вносит свою функциональность — Timestampable предоставляет методы, связанные со временем, в то время как Identifiable обрабатывает уникальные идентификаторы. Этот подход позволяет каждому трейту сосредоточиться на одной ответственности, делая ваш код более модульным и переиспользуемым.
Что происходит, когда два трейта имеют методы с одинаковыми именами? Это конфликтная ситуация, которую мы изучим в следующем уроке.
Задание
ЛегкоДавайте создадим систему управления продуктами, которая объединяет несколько трейтов, чтобы наделить продукты возможностями ценообразования и управления запасами. Каждый трейт будет отвечать за одну задачу, а ваш класс продукта получит функциональность от обоих.
Вы организуете свой код в трех файлах:
Priceable.php— Создайте трейт с именемPriceable, который обрабатывает функциональность ценообразования. Трейт должен иметь:- Приватное свойство
$price(float) - Метод
setPrice(float $price), который сохраняет цену - Метод
getPrice(), который возвращает цену - Метод
getFormattedPrice(), который возвращает цену, отформатированную как"$[price]"с 2 знаками после запятой
- Приватное свойство
Stockable.php— Создайте трейт с именемStockable, который управляет запасами. Трейт должен иметь:- Приватное свойство
$stock(integer) - Метод
setStock(int $quantity), который сохраняет количество товара на складе - Метод
getStock(), который возвращает текущий запас - Метод
isInStock(), который возвращаетtrue, если запас больше 0, иfalseв противном случае
- Приватное свойство
main.php— Подключите оба файла трейтов и создайте классProduct, который использует трейтыPriceableиStockable. Используйте продвижение свойств в конструкторе (constructor promotion) для определения публичного свойства$name. Конструктор также должен принимать цену и количество товара, используя методы трейтов для их установки.Вы получите три входных значения: название продукта, цену и количество на складе. Создайте продукт с этими значениями и выведите четыре строки:
- Название продукта
- Отформатированную цену (используя
getFormattedPrice()) - Количество на складе
"Available", если товар есть в наличии, или"Out of stock", если нет
Это демонстрирует, как несколько трейтов работают вместе — ваш класс Product получает возможности ценообразования от одного трейта и управление запасами от другого, сохраняя каждую задачу четко разделенной и объединяя их в полнофункциональный продукт.
Шпаргалка
Чтобы использовать несколько трейтов в классе, перечислите их через запятую после ключевого слова use:
<?php
trait Timestampable {
public function getTimestamp(): string {
return date('Y-m-d H:i:s');
}
}
trait Identifiable {
private string $id;
public function generateId(): void {
$this->id = uniqid('id_');
}
public function getId(): string {
return $this->id;
}
}
class Post {
use Timestampable, Identifiable;
public function __construct(public string $title) {
$this->generateId();
}
}
$post = new Post("Hello World");
echo $post->getId() . " - " . $post->getTimestamp();
// Output: id_6789abc123 - 2024-01-15 10:30:00
Каждый трейт привносит в класс свою функциональность. Такой подход позволяет каждому трейту сосредоточиться на одной задаче, делая код более модульным и пригодным для повторного использования.
Попробуйте сами
<?php
require_once 'Priceable.php';
require_once 'Stockable.php';
// TODO: Создайте класс Product, который использует трейты Priceable и Stockable
// Используйте продвижение свойств в конструкторе (constructor promotion) для публичного свойства $name
// Конструктор должен принимать name, price и stock, и использовать методы трейтов для установки цены и количества на складе
class Product {
// TODO: Используйте трейты здесь
// TODO: Реализуйте конструктор с продвижением свойств для $name
// и установите цену и количество на складе, используя методы трейтов
}
// Чтение входных данных
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$stock = intval(trim(fgets(STDIN)));
// TODO: Создайте экземпляр Product с входными значениями
// TODO: Выведите название продукта
// TODO: Выведите отформатированную цену, используя getFormattedPrice()
// TODO: Выведите количество на складе
// TODO: Выведите "Available", если товар есть в наличии, или "Out of stock", если нет
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy