register('project_id', (new Common\Implement\Repository\Mapper()) ->setProperty('project') ->setFunction(function($data) { return $this->proyectoRepository->fetchById($data['project_id']); })) ->register('buyer_rut', (new Common\Implement\Repository\Mapper()) ->setProperty('buyer') ->setFunction(function($data) { return $this->personaRepository->fetchById($data['buyer_rut']); })) ->register('date', new Common\Implement\Repository\Mapper\DateTime('date')); return $this->parseData(new Model\Venta\Reservation(), $data, $map); } public function save(Common\Define\Model $model): Model\Venta\Reservation { $model->id = $this->saveNew([ 'project_id', 'buyer_rut', 'date' ], [ $model->project->id, $model->buyer->rut, $model->date->format('Y-m-d') ]); $this->saveUnits($model); $this->savePromotions($model); $this->saveBroker($model); return $model; } /** * @param Common\Define\Model $model * @param array $new_data * @return Model\Venta\Reservation * @throws Common\Implement\Exception\EmptyResult */ public function edit(Common\Define\Model $model, array $new_data): Model\Venta\Reservation { $model = $this->update($model, ['project_id', 'buyer_rut', 'date'], $new_data); $this->editUnits($model, $new_data); $this->editPromotions($model, $new_data); $this->editBroker($model, $new_data); return $model; } public function load(array $data_row): Model\Venta\Reservation { $model = parent::load($data_row); $this->fetchUnits($model, $data_row); $this->fetchBroker($model, $data_row); $this->fetchPromotions($model, $data_row); return $model; } /** * @param int $buyer_rut * @param DateTimeInterface $date * @return Model\Venta\Reservation * @throws Common\Implement\Exception\EmptyResult */ public function fetchByBuyerAndDate(int $buyer_rut, DateTimeInterface $date): Model\Venta\Reservation { $query = $this->connection->getQueryBuilder() ->select() ->from('reservations') ->where('buyer_rut = :buyer_rut AND date >= :date'); return $this->fetchOne($query, ['buyer_rut' => $buyer_rut, 'date' => $date->sub(new DateInterval('P10D'))->format('Y-m-d')]); } public function fetchByProject(int $project_id): array { $query = $this->connection->getQueryBuilder() ->select() ->from('reservations') ->where('project_id = :project_id'); return $this->fetchMany($query, ['project_id' => $project_id]); } /** * @param int $project_id * @param int $state * @return array * @throws Common\Implement\Exception\EmptyResult * @throws InvalidState */ public function fetchState(int $project_id, int $state): array { if (!in_array($state, Model\Venta\Reservation\State\Type::getTypes())) { throw new InvalidState(); } $sub1 = $this->connection->getQueryBuilder() ->select('MAX(id) AS id, reservation_id') ->from('reservation_states') ->group('reservation_id'); $sub2 = $this->connection->getQueryBuilder() ->select('er1.*') ->from('reservation_states er1') ->joined("INNER JOIN ({$sub1}) er0 ON er0.id = er1.id"); $query = $this->connection->getQueryBuilder() ->select() ->from('reservations') ->joined("INNER JOIN ({$sub2}) er ON er.reservation_id = reservations.id") ->where('project_id = :project_id AND er.type = :state'); return $this->fetchMany($query, ['project_id' => $project_id, 'state' => $state]); } /** * @param int $project_id * @return array * @throws Common\Implement\Exception\EmptyResult */ public function fetchActive(int $project_id): array { try { return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::ACTIVE->value); } catch (InvalidState $exception) { throw new Common\Implement\Exception\EmptyResult('Select active reservations', $exception); } } /** * @param int $project_id * @return array * @throws Common\Implement\Exception\EmptyResult */ public function fetchPending(int $project_id): array { try { return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::INACTIVE->value); } catch (InvalidState $exception) { throw new Common\Implement\Exception\EmptyResult('Select pending reservations', $exception); } } /** * @param int $project_id * @return array * @throws Common\Implement\Exception\EmptyResult */ public function fetchRejected(int $project_id): array { try { return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::REJECTED->value); } catch (InvalidState $exception) { throw new Common\Implement\Exception\EmptyResult('Select rejected reservations', $exception); } } protected function saveUnits(Model\Venta\Reservation $reservation): void { if (empty($reservation->units)) { return; } $queryCheck = $this->connection->getQueryBuilder() ->select('COUNT(id) AS cnt') ->from('reservation_details') ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id'); $statementCheck = $this->connection->prepare($queryCheck); $queryInsert = $this->connection->getQueryBuilder() ->insert() ->into('reservation_details') ->columns(['reservation_id', 'type', 'reference_id', 'value']) ->values([':reservation_id', ':type', ':reference_id', ':value']); $statementInsert = $this->connection->prepare($queryInsert); foreach ($reservation->units as $unit) { $statementCheck->execute([ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'unit_id' => $unit->unit->id ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); if ($result['cnt'] > 0) { continue; } $statementInsert->execute([ 'reservation_id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'reference_id' => $unit->unit->id, 'value' => $unit->value ]); } $this->cleanUpUnits($reservation); } protected function cleanUpUnits(Model\Venta\Reservation $reservation): void { $this->cleanUpDetails($reservation, Model\Venta\Reservation\Detail\Type::Unit->value, array_map(fn($unit) => $unit->unit->id, $reservation->units)); } protected function saveBroker(Model\Venta\Reservation &$reservation): void { if ($reservation->broker === null) { $this->removeBroker($reservation); return; } try { $queryCheck = $this->connection->getQueryBuilder() ->select('id') ->from('reservation_details') ->where('reservation_id = :id AND type = :type'); $statementCheck = $this->connection->execute($queryCheck, [ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Broker->value ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); $new_id = $reservation->broker->id; $reservation->broker = $this->brokerRepository->fetchById($result['id']); $this->editBroker($reservation, ['broker_id' => $new_id]); } catch (PDOException) { $queryInsert = $this->connection->getQueryBuilder() ->insert() ->into('reservation_details') ->columns(['reservation_id', 'type', 'reference_id']) ->values([':reservation_id', ':type', ':reference_id']); $this->connection->execute($queryInsert, [ 'reservation_id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Broker->value, 'reference_id' => $reservation->broker->id ]); } } protected function savePromotions(Model\Venta\Reservation $reservation): void { if (empty($reservation->promotions)) { return; } $queryCheck = $this->connection->getQueryBuilder() ->select('COUNT(id) AS cnt') ->from('reservation_details') ->where('reservation_id = :id AND type = :type AND reference_id = :promotion_id'); $statementCheck = $this->connection->prepare($queryCheck); $queryInsert = $this->connection->getQueryBuilder() ->insert() ->into('reservation_details') ->columns(['reservation_id', 'type', 'reference_id']) ->values([':reservation_id', ':type', ':reference_id']); $statementInsert = $this->connection->prepare($queryInsert); foreach ($reservation->promotions as $promotion) { $statementCheck->execute([ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value, 'promotion_id' => $promotion->id ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); if ($result['cnt'] > 0) { continue; } $statementInsert->execute([ 'reservation_id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value, 'reference_id' => $promotion->id ]); } $this->cleanUpPromotions($reservation); } protected function cleanUpPromotions(Model\Venta\Reservation $reservation): void { $this->cleanUpDetails($reservation, Model\Venta\Reservation\Detail\Type::Promotion->value, array_map(fn($promotion) => $promotion->id, $reservation->promotions)); } protected function cleanUpDetails(Model\Venta\Reservation $reservation, int $type_id, array $currentIds): void { $queryCheck = $this->connection->getQueryBuilder() ->select('COUNT(id) AS cnt') ->from('reservation_details') ->where('reservation_id = :id AND type = :type'); $statementCheck = $this->connection->prepare($queryCheck); $deleteParam = implode(', ', array_map(fn($id) => ":id$id", $currentIds)); $queryDelete = $this->connection->getQueryBuilder() ->delete() ->from('reservation_details') ->where("reservation_id = :id AND type = :type AND reference_id NOT IN ({$deleteParam})"); $statementDelete = $this->connection->prepare($queryDelete); try { $statementCheck->execute([ 'id' => $reservation->id, 'type' => $type_id ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); if ($result['cnt'] <= count($currentIds)) { return; } $deleteIdValues = array_combine(array_map(fn($id) => "id$id", $currentIds), $currentIds); $statementDelete->execute(array_merge( ['id' => $reservation->id, 'type' => $type_id], $deleteIdValues)); } catch (PDOException) {} } protected function editUnits(Model\Venta\Reservation &$reservation, array $new_data): void { $querySelect = $this->connection->getQueryBuilder() ->select() ->from('reservation_details') ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id'); $statementSelect = $this->connection->prepare($querySelect); $queryUpdate = $this->connection->getQueryBuilder() ->update('reservation_details') ->set('value = :value') ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id'); $statementUpdate = $this->connection->prepare($queryUpdate); $queryInsert = $this->connection->getQueryBuilder() ->insert() ->into('reservation_details') ->columns(['reservation_id', 'type', 'reference_id', 'value']) ->values([':reservation_id', ':type', ':reference_id', ':value']); $statementInsert = $this->connection->prepare($queryInsert); foreach ($new_data['units'] as $unit) { $idx = $reservation->findUnit($unit['unit_id']); if ($idx === null) { $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']); $statementInsert->execute([ 'reservation_id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'reference_id' => $unit['unit_id'], 'value' => $unit['value'] ]); continue; } $statementSelect->execute([ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'unit_id' => $unit['unit_id'] ]); $result = $statementSelect->fetch(PDO::FETCH_ASSOC); if (!$result) { $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']); $statementInsert->execute([ 'reservation_id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'reference_id' => $unit['unit_id'], 'value' => $unit['value'] ]); continue; } $statementUpdate->execute([ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'unit_id' => $unit['unit_id'], 'value' => $unit['value'] ]); $reservation->units[$idx]['value'] = $unit['value']; } } protected function editBroker(Model\Venta\Reservation &$reservation, array $new_data): void { if (!array_key_exists('broker_id', $new_data) or $new_data['broker_id'] === $reservation->broker->id) { return; } try { $query = $this->connection->getQueryBuilder() ->update('reservation_details') ->set('reference_id = :broker_id') ->where('reservation_id = :id AND type = :type'); $this->connection->execute($query, [ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Broker->value, 'broker_id' => $new_data['broker_id'] ]); $reservation->broker = $this->brokerRepository->fetchById($new_data['broker_id']); } catch (PDOException) {} } protected function editPromotions(Model\Venta\Reservation &$reservation, array $new_data): void { $querySelect = $this->connection->getQueryBuilder() ->select() ->from('reservation_details') ->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id'); $statementSelect = $this->connection->prepare($querySelect); $queryUpdate = $this->connection->getQueryBuilder() ->update('reservation_details') ->set('value = :value') ->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id'); $statementUpdate = $this->connection->prepare($queryUpdate); foreach ($new_data as $promotion_id => $value) { $idx = array_search($promotion_id, array_column($reservation->promotions, 'id')); if ($idx === false) { $reservation->promotions []= $this->promotionRepository->fetchById($promotion_id); continue; } $statementSelect->execute(['id' => $reservation->id, 'promotion_id' => $promotion_id]); $result = $statementSelect->fetch(PDO::FETCH_ASSOC); if (!$result) { continue; } $statementUpdate->execute(['id' => $reservation->id, 'promotion_id' => $promotion_id, 'value' => $value]); $reservation->promotions[$idx] = $this->promotionRepository->fetchById($promotion_id); } } protected function fetchUnits(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation { $this->fetchSavedUnits($reservation); if (array_key_exists('units', $new_data) and count($new_data['units']) > 0) { $this->fetchUnsavedUnits($reservation, $new_data); } return $reservation; } protected function fetchSavedUnits(Model\Venta\Reservation &$reservation): Model\Venta\Reservation { $query = $this->connection->getQueryBuilder() ->select() ->from('reservation_details') ->where('reservation_id = :id AND type = ?'); $statement = $this->connection->execute($query, [ 'id' => $reservation->id, Model\Venta\Reservation\Detail\Type::Unit->value ]); while ($result = $statement->fetch(PDO::FETCH_ASSOC)) { try { $reservation->addUnit($this->unitRepository->fetchById($result['reference_id']), $result['value']); } catch (Common\Implement\Exception\EmptyResult) {} } return $reservation; } protected function fetchUnsavedUnits(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation { if (!array_key_exists('units', $new_data) or count($new_data['units']) > 0) { return $reservation; } $queryCheck = $this->connection->getQueryBuilder() ->select('COUNT(id) AS cnt') ->from('reservation_details') ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id'); $statementCheck = $this->connection->prepare($queryCheck); foreach ($new_data['units'] as $unit) { $statementCheck->execute([ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Unit->value, 'unit_id' => $unit['unit_id'] ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); if ($result['cnt'] > 0) { continue; } try { $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']); } catch (Common\Implement\Exception\EmptyResult) {} } return $reservation; } protected function fetchBroker(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation { if (!array_key_exists('broker_id', $new_data)) { $query = $this->connection->getQueryBuilder() ->select('reference_id') ->from('reservation_details') ->where('reservation_id = :id AND type = :type'); try { $statement =$this->connection->execute($query, [ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Broker->value ]); $result = $statement->fetch(PDO::FETCH_ASSOC); $reservation->broker = $this->brokerRepository->fetchById($result['reference_id']); } catch (PDOException) {} return $reservation; } try { $reservation->broker = $this->brokerRepository->fetchById($new_data['broker_id']); } catch (Common\Implement\Exception\EmptyResult) {} return $reservation; } protected function fetchPromotions(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation { $this->fetchSavedPromotions($reservation); $this->fetchUnsavedPromotions($reservation, $new_data); return $reservation; } protected function fetchSavedPromotions(Model\Venta\Reservation &$reservation): Model\Venta\Reservation { $query = $this->connection->getQueryBuilder() ->select() ->from('reservation_details') ->where('type = :type AND reservation_id = :id'); $statement = $this->connection->execute($query, ['id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value]); while ($result = $statement->fetch(PDO::FETCH_ASSOC)) { try { $reservation->promotions []= $this->promotionRepository->fetchById($result['reference_id']); } catch (Common\Implement\Exception\EmptyResult) {} } return $reservation; } protected function fetchUnsavedPromotions(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation { if (!array_key_exists('promotions', $new_data) or count($new_data['promotions']) > 0) { return $reservation; } $queryCheck = $this->connection->getQueryBuilder() ->select('COUNT(id) AS cnt') ->from('reservation_details') ->where('type = :type AND reservation_id = :id AND reference_id = :promotion_id'); $statementCheck = $this->connection->prepare($queryCheck); foreach ($new_data['promotions'] as $promotion) { $statementCheck->execute([ 'id' => $reservation->id, 'promotion_id' => $promotion ]); $result = $statementCheck->fetch(PDO::FETCH_ASSOC); if ($result['cnt'] > 0) { continue; } try { $reservation->promotions []= $this->promotionRepository->fetchById($promotion); } catch (Common\Implement\Exception\EmptyResult) {} } return $reservation; } protected function removeBroker(Model\Venta\Reservation &$reservation): void { try { $query = $this->connection->getQueryBuilder() ->delete() ->from('reservation_details') ->where('reservation_id = :id AND type = :type'); $this->connection->execute($query, [ 'id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Broker->value ]); $reservation->broker = null; } catch (PDOException) {} } }