diff --git a/design_patterns/abstract_factory/.env b/design_patterns/abstract_factory/.env new file mode 100644 index 0000000..5d735a7 --- /dev/null +++ b/design_patterns/abstract_factory/.env @@ -0,0 +1,6 @@ +DB_DRIVER=pgsql +DB_HOST=db +DB_PORT=5432 +DB_DATABASE=movie +DB_USERNAME=root +DB_PASSWORD=password \ No newline at end of file diff --git a/design_patterns/abstract_factory/Container.php b/design_patterns/abstract_factory/Container.php new file mode 100644 index 0000000..dd1f99f --- /dev/null +++ b/design_patterns/abstract_factory/Container.php @@ -0,0 +1,15 @@ + new MySqlFactory(), + 'pgsql' => new PgSqlFactory(), + default => throw new RuntimeException('Неизвестный драйвер базы данных'), + }; + } +} \ No newline at end of file diff --git a/design_patterns/abstract_factory/Interfaces/DbConnectionInterface.php b/design_patterns/abstract_factory/Interfaces/DbConnectionInterface.php new file mode 100644 index 0000000..6b0b8b5 --- /dev/null +++ b/design_patterns/abstract_factory/Interfaces/DbConnectionInterface.php @@ -0,0 +1,12 @@ +pdo !== null) { + return; + } + + $dsn = "mysql:host={$config['host']};dbname={$config['db']};charset={$config['charset']}"; + $this->pdo = new PDO($dsn, $config['user'], $config['password'], [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } + + public function execute(string $sql, array $bindings = []): array + { + if ($this->pdo === null) { + throw new RuntimeException('Ошибка подключения к базе данных'); + } + $stmt = $this->pdo->prepare($sql); + $stmt->execute($bindings); + return $stmt->fetchAll(); + } + + public function getPdo(): PDO + { + return $this->pdo; + } +} diff --git a/design_patterns/abstract_factory/MySQL/MySQLQueryBuilder.php b/design_patterns/abstract_factory/MySQL/MySQLQueryBuilder.php new file mode 100644 index 0000000..f50951d --- /dev/null +++ b/design_patterns/abstract_factory/MySQL/MySQLQueryBuilder.php @@ -0,0 +1,39 @@ +table = $table; + + return $this; + } + + public function limit(int $limit): self + { + $this->limit = "LIMIT {$limit}"; + + return $this; + } + + public function paginate(int $limit, int $offset): self + { + $this->limit = "LIMIT {$offset}, {$limit}"; + + return $this; + } + + public function get(): string + { + $sql = "SELECT * FROM {$this->table} {$this->limit}"; + + $this->table = ''; + $this->limit = ''; + + return $sql; + } +} diff --git a/design_patterns/abstract_factory/MySQL/MySQLTransaction.php b/design_patterns/abstract_factory/MySQL/MySQLTransaction.php new file mode 100644 index 0000000..4860e22 --- /dev/null +++ b/design_patterns/abstract_factory/MySQL/MySQLTransaction.php @@ -0,0 +1,22 @@ +connection->getPdo()->beginTransaction(); + } + public function commit(): void + { + $this->connection->getPdo()->commit(); + } + public function rollback(): void + { + $this->connection->getPdo()->rollBack(); + } +} \ No newline at end of file diff --git a/design_patterns/abstract_factory/MySQL/MySqlFactory.php b/design_patterns/abstract_factory/MySQL/MySqlFactory.php new file mode 100644 index 0000000..24957d4 --- /dev/null +++ b/design_patterns/abstract_factory/MySQL/MySqlFactory.php @@ -0,0 +1,25 @@ +connection === null) { + $this->connection = new MySQLConnection(); + } + return $this->connection; + } + public function queryBuilder(): QueryBuilderInterface + { + return new MySQLQueryBuilder(); + } + + public function transaction(): TransactionInterface + { + return new MySQLTransaction($this->connection()); + } +} \ No newline at end of file diff --git a/design_patterns/abstract_factory/PgSQL/PgSQLConnection.php b/design_patterns/abstract_factory/PgSQL/PgSQLConnection.php new file mode 100644 index 0000000..6531c7b --- /dev/null +++ b/design_patterns/abstract_factory/PgSQL/PgSQLConnection.php @@ -0,0 +1,38 @@ +pdo !== null) { + return; + } + + $dsn = "pgsql:host={$config['host']};dbname={$config['db']};charset={$config['charset']}"; + $this->pdo = new PDO($dsn, $config['user'], $config['password'], + [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } + + public function execute(string $sql, array $bindings = []): array + { + if ($this->pdo === null) { + throw new RuntimeException('Ошибка подключения к базе данных'); + } + + $stmt = $this->pdo->prepare($sql); + $stmt->execute($bindings); + return $stmt->fetchAll(); + } + + public function getPdo(): PDO + { + return $this->pdo; + } +} diff --git a/design_patterns/abstract_factory/PgSQL/PgSQLQueryBuilder.php b/design_patterns/abstract_factory/PgSQL/PgSQLQueryBuilder.php new file mode 100644 index 0000000..fe569bf --- /dev/null +++ b/design_patterns/abstract_factory/PgSQL/PgSQLQueryBuilder.php @@ -0,0 +1,40 @@ +table = $table; + + return $this; + } + + public function limit(int $limit): self + { + $this->limit = "LIMIT {$limit}"; + + return $this; + } + + public function paginate(int $limit, int $offset): self + { + $this->limit = "LIMIT {$limit} OFFSET {$offset}"; + + return $this; + } + + public function get(): string + { + $sql = "SELECT * FROM {$this->table} {$this->limit}"; + + $this->table = ''; + $this->limit = ''; + + return $sql; + } +} diff --git a/design_patterns/abstract_factory/PgSQL/PgSQLTransaction.php b/design_patterns/abstract_factory/PgSQL/PgSQLTransaction.php new file mode 100644 index 0000000..c0a8c86 --- /dev/null +++ b/design_patterns/abstract_factory/PgSQL/PgSQLTransaction.php @@ -0,0 +1,22 @@ +connection->getPdo()->beginTransaction(); + } + public function commit(): void + { + $this->connection->getPdo()->commit(); + } + public function rollback(): void + { + $this->connection->getPdo()->rollBack(); + } +} \ No newline at end of file diff --git a/design_patterns/abstract_factory/PgSQL/PgSqlFactory.php b/design_patterns/abstract_factory/PgSQL/PgSqlFactory.php new file mode 100644 index 0000000..51e26c7 --- /dev/null +++ b/design_patterns/abstract_factory/PgSQL/PgSqlFactory.php @@ -0,0 +1,25 @@ +connection === null) { + $this->connection = new PgSQLConnection(); + } + return $this->connection; + } + public function queryBuilder(): QueryBuilderInterface + { + return new PgSQLQueryBuilder(); + } + + public function transaction(): TransactionInterface + { + return new PgSQLTransaction($this->connection); + } +} \ No newline at end of file diff --git a/design_patterns/abstract_factory/index.php b/design_patterns/abstract_factory/index.php new file mode 100644 index 0000000..5a4ef55 --- /dev/null +++ b/design_patterns/abstract_factory/index.php @@ -0,0 +1,37 @@ + env('DB_DRIVER', 'pgsql'), + 'host' => env('DB_HOST', 'localhost'), + 'db' => env('DB_DATABASE', 'localhost'), + 'port' => env('DB_PORT'), + 'user' => env('DB_USERNAME'), + 'password' => env('DB_PASSWORD'), + 'charset' => env('utf8mb4') +]; + +$factory = Container::make($config['driver']); + +$connection = $factory->connection(); +$connection->connect($config); + +//Пример запроса +$sql = $factory->queryBuilder() + ->select('movie') + ->paginate(10, 20) + ->get(); + + +$movie = $connection->execute($sql); + +// Пример транзакции +$transaction = $factory->transaction(); +$transaction->start(); +try { +// Что-то реализуем + $transaction->commit(); +} catch (\Exception $e) { + $transaction->rollback(); +} \ No newline at end of file diff --git a/design_patterns/observer/BonusListener.php b/design_patterns/observer/BonusListener.php new file mode 100644 index 0000000..30b3daf --- /dev/null +++ b/design_patterns/observer/BonusListener.php @@ -0,0 +1,11 @@ +userId} начислено 100 бонусов."; + } +} diff --git a/design_patterns/observer/EmailListener.php b/design_patterns/observer/EmailListener.php new file mode 100644 index 0000000..e22aaae --- /dev/null +++ b/design_patterns/observer/EmailListener.php @@ -0,0 +1,11 @@ +email}"; + } +} diff --git a/design_patterns/observer/EventDispatcher.php b/design_patterns/observer/EventDispatcher.php new file mode 100644 index 0000000..d7f6f3e --- /dev/null +++ b/design_patterns/observer/EventDispatcher.php @@ -0,0 +1,23 @@ +listeners[$eventClass][] = $listener; + } + + public function dispatch(object $event) + { + $eventClass = get_class($event); + + $listenerList = $this->listeners[$eventClass] ?? []; + + foreach ($listenerList as $listener) { + $listener->handle($event); + } + } +} diff --git a/design_patterns/observer/ListenerInterface.php b/design_patterns/observer/ListenerInterface.php new file mode 100644 index 0000000..8d9591e --- /dev/null +++ b/design_patterns/observer/ListenerInterface.php @@ -0,0 +1,8 @@ +userId} успешно зарегистрирован."; + } +} diff --git a/design_patterns/observer/index.php b/design_patterns/observer/index.php new file mode 100644 index 0000000..dbc0a0f --- /dev/null +++ b/design_patterns/observer/index.php @@ -0,0 +1,18 @@ +listen(UserRegistered::class, new EmailListener()); +$action->listen(UserRegistered::class, new BonusListener()); +$action->listen(UserRegistered::class, new LogListener()); + +$action->dispatch(new UserRegistered(1, 'example@mail.ru')); \ No newline at end of file diff --git a/design_patterns/query_builder/BuilderInterface.php b/design_patterns/query_builder/BuilderInterface.php new file mode 100644 index 0000000..5fff31e --- /dev/null +++ b/design_patterns/query_builder/BuilderInterface.php @@ -0,0 +1,24 @@ +fields = $fields; + + return $this; + } + + public function from(string $table): self + { + $this->table = $table; + + return $this; + } + + public function join(string $table, string $on): self + { + $this->joins[] = "JOIN {$table} ON {$on}"; + + return $this; + } + + public function where(string $condition): self + { + if (! empty($this->where)) { + throw new LogicException('where() уже был вызван. Используйте andWhere()'); + } + + $this->where[] = $condition; + + return $this; + } + + public function andWhere(string $condition): self + { + $this->where[] = 'AND ' . $condition; + + return $this; + } + + public function orderBy(string $field, string $direction = 'ASC'): self + { + $direction = strtoupper($direction); + + if (! in_array($direction, ['ASC', 'DESC'])) { + throw new InvalidArgumentException('Ошибка в выборе инструкции для сортировки'); + } + + $this->orderBy = "ORDER BY {$field} {$direction}"; + + return $this; + } + + public function limit(int $limit, ?int $offset = null): self + { + if ($offset !== null) { + $this->limit = "LIMIT {$offset}, {$limit}"; + } else { + $this->limit = "LIMIT {$limit}"; + } + return $this; + } + + public function build(): string + { + if (empty($this->fields)) { + throw new \Exception("Ошибка сборки запроса: не указаны поля (SELECT)."); + } + if (empty($this->table)) { + throw new \Exception("Ошибка сборки запроса: не указана таблица (FROM)."); + } + + $sql = "SELECT " . implode(', ', $this->fields) . " FROM " . $this->table; + + if (! empty($this->joins)) { + $sql .= " " . implode(' ', $this->joins); + } + + if (! empty($this->where)) { + $sql .= ' WHERE ' . implode(' ', $this->where); + } + + if (! empty($this->orderBy)) { + $sql .= " " . $this->orderBy; + } + + if (! empty($this->limit)) { + $sql .= " " . $this->limit; + } + + $result = $sql; + + $this->reset(); + + return $result; + } + + public function reset(): self + { + $this->fields = []; + $this->table = ''; + $this->where = []; + $this->joins = []; + $this->orderBy = ''; + $this->limit = ''; + + return $this; + } +} diff --git a/design_patterns/strategy/CourierDelivery.php b/design_patterns/strategy/CourierDelivery.php new file mode 100644 index 0000000..9bf4919 --- /dev/null +++ b/design_patterns/strategy/CourierDelivery.php @@ -0,0 +1,13 @@ +service = $service; + } + + public function calculate(array $order): float + { + return $this->service->calc($order); + } +} \ No newline at end of file diff --git a/design_patterns/strategy/DeliveryInterface.php b/design_patterns/strategy/DeliveryInterface.php new file mode 100644 index 0000000..9bbb492 --- /dev/null +++ b/design_patterns/strategy/DeliveryInterface.php @@ -0,0 +1,8 @@ + new CourierDelivery(), + 'pickup' => new PickupDelivery(), + 'post' => new PostDelivery(), +]; + +$config = [ + 'driver' => 'post', +]; + +$order = [ + 'product_id' => 1, + 'address' => 'Moscow', + 'weight' => 2, + 'tariff' => 0.22, + 'kv' => 1, + 'distance' => 15, + 'quantity' => 10, +]; + +// Расчет 1 доставки +$calculator = new DeliveryCalculatorService($deliveryMapper[$config['driver']]); +$price = $calculator->calculate($order); + +// Расчет по всем доставкам +$allPrices = []; + +foreach ($deliveryMapper as $name => $class) { + $calculator->set($class); + $allPrices[$name] = $calculator->calculate($order); +} + +print_r($allPrices); diff --git a/design_patterns/template_method/AbstractImporter.php b/design_patterns/template_method/AbstractImporter.php new file mode 100644 index 0000000..7ab3d0c --- /dev/null +++ b/design_patterns/template_method/AbstractImporter.php @@ -0,0 +1,49 @@ +load($this->data); + $parsedData = $this->parse($loadedData); + $validData = $this->validate($parsedData); + $entities = $this->transform($validData); + $this->beforeSave($entities); + return $this->saveToDb($entities); + } catch (\Exception $e) { + return [ + 'success' => false, + 'data' => 'Ошибка импорта: ' . $e->getMessage() + ]; + } + } + + abstract protected function load(string $loaded); + + abstract protected function parse(string $loadedData); + + abstract protected function validate(array $parsedData); + + abstract protected function transform(array $validData); + + protected function saveToDb(array $entities): array + { + return [ + 'success' => true, + 'data' => "Успешно сохранено в БД " . count($entities) . " объектов", + ]; + } + + protected function beforeSave(array $entities): void + { + + } +} \ No newline at end of file diff --git a/design_patterns/template_method/CSVImporter.php b/design_patterns/template_method/CSVImporter.php new file mode 100644 index 0000000..6a30348 --- /dev/null +++ b/design_patterns/template_method/CSVImporter.php @@ -0,0 +1,53 @@ + $validData['fio'], + 'pasport' => $validData['pasport'], + 'phone' => $validData['phone'], + ]; + } + + protected function beforeSave(array $entities): void + { + $path = __DIR__ . "/csv_data_{$entities['orderId']}.txt"; + file_put_contents($path, $entities); + } +} \ No newline at end of file diff --git a/design_patterns/template_method/JsonImporter.php b/design_patterns/template_method/JsonImporter.php new file mode 100644 index 0000000..9c43696 --- /dev/null +++ b/design_patterns/template_method/JsonImporter.php @@ -0,0 +1,39 @@ + $validData['orderId'], + 'amount' => $validData['amount'], + 'merchant' => $validData['merchant'], + ]; + } +} \ No newline at end of file diff --git a/design_patterns/template_method/XMLImporter.php b/design_patterns/template_method/XMLImporter.php new file mode 100644 index 0000000..aba3686 --- /dev/null +++ b/design_patterns/template_method/XMLImporter.php @@ -0,0 +1,44 @@ + $validData['userId'], + 'email' => $validData['email'], + 'phone' => $validData['phone'], + ]; + } +} \ No newline at end of file