Полезные материалы по PHP, Laravel, Symfony, CodeIgniter, CakePHP, Phalcon
По всем вопросам @evgenycarter
Post #846
820
БИ @php_lib
Showing posts older than #847 · Back to latest



diffInDaysFiltered»diffInDaysFiltered», чтобы сделать именно это 🚀.Order, в которой только геттеры и сеттеры. А вся логика лежит в гигантском OrderService на 2000 строк.
// Service
$order->setStatus('paid');
$order->setUpdatedAt(new DateTime());
// А вдруг забыли отправить ивент?
// А можно ли менять статус на 'paid', если сумма 0?
// Сервис должен всё помнить.
$repo->save($order);
class Order
{
// Свойства приватны! Извне их менять нельзя.
private string $status;
private array $items = [];
public function pay(Payment $payment): void
{
if ($this->status === 'paid') {
throw new DomainException("Order already paid");
}
if ($payment->amount < $this->getTotal()) {
throw new DomainException("Not enough money");
}
$this->status = 'paid';
$this->recordEvent(new OrderPaid($this->id));
}
}
// Service становится тонким и скучным (и это хорошо!):
$order->pay($payment);
$repo->save($order);
function register(string $email, string $phone, float $amount)string $email - это просто набор байт. Там может быть "asdf", и PHP это проглотит.
readonly class Email
{
public string $value;
public function __construct(string $value)
{
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email format");
}
$this->value = strtolower($value); // Нормализация
}
public function getDomain(): string
{
return explode('@', $this->value)[1];
}
}
// Теперь сигнатура метода говорит правду:
function register(Email $email, Phone $phone, Money $amount) { ... }
Email, он 100% валиден. Не надо проверять это снова.Helper::formatPhone().$user->points -= 10. Это рассинхрон, который приведет к багам. В DDD это будет $user->debitBonuses(10).gender (M / F) почти бесполезен. Базе проще прочитать всю таблицу (Full Scan), чем прыгать по индексу, который отсеет всего 50% записей.(group_id, status) сработает для WHERE group_id = 5.WHERE status = active.WHERE YEAR(created_at) = 2026 - индекс по created_at игнорируется.WHERE created_at >= '2026-01-01' - индекс работает.LIKE '%text' - Full Scan. Индекс бесполезен.LIKE 'text%' - Range Scan. Индекс работает.EXPLAIN (или EXPLAIN ANALYZE) на сложных запросах перед выкаткой. Доверяй, но проверяй.
Storage::download(), чтобы принудительно запустить скачивание файла в браузере. Отлично подходит для защищенных файлов или генерации динамических загрузок.
Password::defaults().