Commit fed644a6 authored by Karamel's avatar Karamel
Browse files

Simplify embedded model API with a better use of Doctrine (deleting SubclassHelper).

parent 6a124edc
......@@ -53,13 +53,6 @@ class PaymentmodeAPI extends APIRefHelper
}
}
}
// Delete previously registered return and values
$returnsHelper = new SubclassHelper($this->dao,
PaymentModeReturn::class, 'returns', 'paymentMode');
$returnsHelper->deleteSubrecords($data);
$valuesHelper = new SubclassHelper($this->dao,
PaymentModeValue::class, 'values', 'paymentMode');
$valuesHelper->deleteSubrecords($data);
/* Delete unlinked value images:
* remove from $images all those which still exists,
* all the remaining are orphaned images. */
......@@ -94,30 +87,4 @@ class PaymentmodeAPI extends APIRefHelper
// Write and commit PaymentModes
return parent::write(($arrayArgs) ? $data : $data[0]);
}
public function delete($id) {
// Same as APIHelper::delete but with delete returns and values loop.
$id = (is_array($id)) ? $id : array($id);
$references = array();
foreach ($id as $i) {
$model = $this->dao->read(static::MODEL_NAME, $i);
if ($model != null) {
$references[] = $model;
}
}
$returnsHelper = new SubclassHelper($this->dao,
PaymentModeReturn::class, 'returns', 'paymentMode');
$returnsHelper->deleteSubrecords($references);
$returnsHelper->clearSubrecords($references);
$valuesHelper = new SubclassHelper($this->dao,
PaymentModeValue::class, 'values', 'paymentMode');
$valuesHelper->deleteSubrecords($references);
$valuesHelper->clearSubrecords($references);
// Delete main records
foreach ($references as $ref) {
$this->dao->delete($ref);
}
$this->dao->commit();
return count($references);
}
}
......@@ -30,39 +30,4 @@ class PlaceAPI extends APIHelper
{
const MODEL_NAME = 'Pasteque\Server\Model\Floor';
const DEFAULT_ORDER = 'dispOrder';
// Same as APIHelper::write but delete all places for replacement.
public function write($data) {
$this->supportOrDie($data);
$arrayArgs = is_array($data);
$data = ($arrayArgs) ? $data : array($data);
// Delete previously registered places
$placesHelper = new SubclassHelper($this->dao,
Place::class, 'places', 'floor');
$placesHelper->deleteSubrecords($data);
return parent::write(($arrayArgs) ? $data : $data[0]);
}
public function delete($id) {
// Same as APIHelper::delete but with delete places loop.
$id = (is_array($id)) ? $id : array($id);
$references = array();
foreach ($id as $i) {
$model = $this->dao->read(static::MODEL_NAME, $i);
if ($model != null) {
$references[] = $model;
}
}
// Delete places before deleting the floor
$placesHelper = new SubclassHelper($this->dao,
Place::class, 'places', 'floor');
$placesHelper->deleteSubrecords($references);
$placesHelper->clearSubrecords($references);
// Delete main records
foreach ($references as $ref) {
$this->dao->delete($ref);
}
$this->dao->commit();
return count($references);
}
}
<?php
// Pastèque API
//
// Copyright (C)
// 2012 Scil (http://scil.coop)
// 2017 Karamel, Association Pastèque (karamel@creativekara.fr, https://pasteque.org)
//
// This file is part of Pastèque.
//
// Pastèque is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pastèque is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pastèque. If not, see <http://www.gnu.org/licenses/>.
namespace Pasteque\Server\API;
use \Pasteque\Server\System\DAO\DAOCondition;
use \Pasteque\Server\System\DAO\DAOFactory;
/** Helper to manipulate subclasses arrays.
* Works only with DoctrineModel with getters and setters
* and a clear<SubclassField>() function.
* This helper is to be instanciated in main API class when required
* and not to be used as a superclass (decorator pattern). */
class SubclassHelper
{
private $dao;
/** Name of the subclass field (name of the array attriubte) */
private $subclassField;
/** Name of the field in subclass that holds the id of the main class */
private $invertedField;
/** Name of the subclass model */
private $subclassName;
public function __construct($dao, $subclassName, $subclassField, $invertedField) {
$this->dao = $dao;
$this->subclassName = $subclassName;
$this->subclassField = $subclassField;
$this->invertedField = $invertedField;
}
/** Order the DAO to delete all subinstances from records from database
* that are not linked in $mainrecords.
* It doesn't commit changes nor clear the subclass collection
* from the main records. */
public function cleanSubrecords($mainrecords) {
// Delete subclasses instances, usefull before deleting the main class
/* Required because the owning side in Doctrine is on subclass
* thus cascade deletes main record when deleting them and not
* the contrary */
$mainrecords = (is_array($mainrecords)) ? $mainrecords : array($mainrecords);
$subrecs = array();
$getName = 'get' . ucfirst($this->subclassField);
foreach ($mainrecords as $rec) {
if ($rec->getId() === null) {
// New record, nothing special to do
continue;
}
// Get subrecords from database
$subrecords = $this->dao->search($this->subclassName,
new DAOCondition($this->invertedField, '=', $rec));
// Look for it in $rec, if not found add it to delete candidates
try {
foreach ($subrecords as $subrecord) {
$reflectionGet = new \ReflectionMethod($rec, $getName);
$mainSubs = $reflectionGet->invoke($rec);
$found = false;
foreach ($mainSubs as $mainSub) {
if ($mainSub->getId() === $subrecord->getId()) {
$found = true;
break;
}
}
if (!$found) {
$subrecs[] = $subrecord;
}
}
} catch (\ReflectionException $e) {
throw new \InvalidArgumentException(sprintf('%s does not implements %s',
get_class($rec), $getName));
}
}
// Delete candidates
foreach ($subrecs as $record) {
$this->dao->delete($record);
}
}
/** Order the DAO to delete all subinstances from records from database.
* It doesn't read subrecords from $mainrecords but will
* fetch for existing data in database and delete them.
* It doesn't commit changes nor clear the subclass collection
* from the main records. */
public function deleteSubrecords($mainrecords) {
// Delete subclasses instances, usefull before deleting the main class
/* Required because the owning side in Doctrine is on subclass
* thus cascade deletes main record when deleting them and not
* the contrary */
$mainrecords = (is_array($mainrecords)) ? $mainrecords : array($mainrecords);
$subrecs = array();
foreach ($mainrecords as $rec) {
if ($rec->getId() === null) {
// New record, nothing special to do
continue;
}
$subrecords = $this->dao->search($this->subclassName,
new DAOCondition($this->invertedField, '=', $rec));
foreach ($subrecords as $subrecord) {
$subrecs[] = $subrecord;
}
}
foreach ($subrecs as $record) {
$this->dao->delete($record);
}
}
/** Call clear<field> on records. Call this after deleteSubrecords
* to delete all subrecords also from $records.
* It must not be called when deleting only previously written subrecords
* and keeping $records state for further update. */
public function clearSubrecords($mainrecords) {
$mainrecords = (is_array($mainrecords)) ? $mainrecords : array($mainrecords);
$clearName = 'clear' . ucfirst($this->subclassField);
foreach ($mainrecords as $rec) {
$reflectionClear = null;
try {
$reflectionClear = new \ReflectionMethod($rec, $clearName);
} catch (\ReflectionException $e) {
throw new \InvalidArgumentException(sprintf('%s does not implements %s', get_class($rec), $getterName));
}
$reflectionClear->invoke($rec);
}
}
}
......@@ -31,39 +31,4 @@ class TariffareaAPI extends APIRefHelper
{
const MODEL_NAME = 'Pasteque\Server\Model\TariffArea';
const DEFAULT_ORDER = 'dispOrder';
// Same as APIHelper::write but delete all price for replacement.
public function write($data) {
$this->supportOrDie($data);
$arrayArgs = is_array($data);
$data = ($arrayArgs) ? $data : array($data);
// Delete previously registered prices
$pricesHelper = new SubclassHelper($this->dao,
TariffAreaPrice::class, 'prices', 'tariffArea');
$pricesHelper->deleteSubrecords($data);
return parent::write(($arrayArgs) ? $data : $data[0]);
}
public function delete($id) {
// Same as APIHelper::delete but with delete prices loop.
$id = (is_array($id)) ? $id : array($id);
$references = array();
foreach ($id as $i) {
$model = $this->dao->read(static::MODEL_NAME, $i);
if ($model != null) {
$references[] = $model;
}
}
// Delete prices before deleting the area
$pricesHelper = new SubclassHelper($this->dao,
TariffAreaPrice::class, 'prices', 'tariffArea');
$pricesHelper->deleteSubrecords($references);
$pricesHelper->clearSubrecords($references);
// Delete main records
foreach ($references as $ref) {
$this->dao->delete($ref);
}
$this->dao->commit();
return count($references);
}
}
......@@ -91,17 +91,20 @@ class Floor extends DoctrineModel
public function setDispOrder($dispOrder) { $this->dispOrder = $dispOrder; }
/**
* @OneToMany(targetEntity="\Pasteque\Server\Model\Place", mappedBy="floor", cascade={"persist"}, orphanRemoval=true) */
* @OneToMany(targetEntity="\Pasteque\Server\Model\Place", mappedBy="floor", cascade={"all"}, orphanRemoval=true) */
protected $places;
public function getPlaces() { return $this->places; }
public function setPlaces($places) {
$this->places->clear();
$this->clearPlaces();
foreach ($places as $place) {
$this->addPlace($place);
}
}
public function clearPlaces() {
$this->getPlaces()->clear();
foreach ($this->places->getKeys() as $key) {
$this->places->get($key)->setFloor(null);
$this->places->remove($key);
}
}
public function addPlace($place) {
$this->places->add($place);
......
......@@ -132,14 +132,13 @@ class PaymentMode extends DoctrineModel
}
/**
* @OneToMany(targetEntity="\Pasteque\Server\Model\PaymentModeValue", mappedBy="paymentMode", cascade={"persist"}, orphanRemoval=true)
* @OneToMany(targetEntity="\Pasteque\Server\Model\PaymentModeValue", mappedBy="paymentMode", cascade={"all"}, orphanRemoval=true)
* @OrderBy({"value" = "DESC"})
*/
protected $values;
public function getValues() { return $this->values; }
public function setValues($values) {
$this->values->clear();
$this->clearValues();
foreach ($values as $value) {
$this->addValue($value);
}
......@@ -149,7 +148,10 @@ class PaymentMode extends DoctrineModel
$value->setPaymentMode($this);
}
public function clearValues() {
$this->values->clear();
foreach ($this->values->getKeys() as $key) {
$this->values->get($key)->setPaymentMode(null);
$this->values->remove($key);
}
}
public function removeValue($value) {
$this->values->removeElement($value);
......@@ -157,13 +159,13 @@ class PaymentMode extends DoctrineModel
}
/**
* @OneToMany(targetEntity="\Pasteque\Server\Model\PaymentModeReturn", mappedBy="paymentMode", cascade={"persist"}, orphanRemoval=true)
* @OneToMany(targetEntity="\Pasteque\Server\Model\PaymentModeReturn", mappedBy="paymentMode", cascade={"all"}, orphanRemoval=true)
* @OrderBy({"minAmount" = "DESC"})
*/
protected $returns;
public function getReturns() { return $this->returns; }
public function setReturns($returns) {
$this->returns->clear();
$this->clearReturns();
foreach ($returns as $return) {
$this->addReturn($return);
}
......@@ -176,7 +178,10 @@ class PaymentMode extends DoctrineModel
}
}
public function clearReturns() {
$this->returns->clear();
foreach ($this->returns->getKeys() as $key) {
$this->returns->get($key)->setPaymentMode(null);
$this->returns->remove($key);
}
}
public function removeReturn($return) {
$this->returns->removeElement($return);
......
......@@ -105,17 +105,20 @@ class TariffArea extends DoctrineModel
public function setDispOrder($dispOrder) { $this->dispOrder = $dispOrder; }
/**
* @OneToMany(targetEntity="\Pasteque\Server\Model\TariffAreaPrice", mappedBy="tariffArea", cascade={"persist"}, orphanRemoval=true) */
* @OneToMany(targetEntity="\Pasteque\Server\Model\TariffAreaPrice", mappedBy="tariffArea", cascade={"all"}, orphanRemoval=true) */
protected $prices;
public function getPrices() { return $this->prices; }
public function setPrices($prices) {
$this->prices->clear();
$this->clearPrices();
foreach ($prices as $price) {
$this->addPrice($price);
}
}
public function clearPrices() {
$this->getPrices()->clear();
foreach ($this->prices->getKeys() as $key) {
$this->prices->get($key)->setTariffArea(null);
$this->prices->remove($key);
}
}
/** Add a TariffAreaPrice if not useless. */
public function addPrice($price) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment