partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit 506c203ffce15004cdbdda48d03d690312efdb77
parent f3474b932ccbe912e7597ea2a4e3035874b819c5
Author: Felicitus <felicitus@felicitus.org>
Date:   Mon,  8 Jun 2015 22:00:05 +0200

Implemented initial doctrine API via REST

Diffstat:
Msrc/PartKeepr/DoctrineReflectionBundle/Controller/DoctrineRESTQueryController.php | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Asrc/PartKeepr/DoctrineReflectionBundle/Exception/ExceptionWrapperHandler.php | 29+++++++++++++++++++++++++++++
Msrc/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig | 17+++++++++++++----
Msrc/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php | 28+++++++++++++++++++++++++---
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/Editor.js | 1+
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js | 7+++++--
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Data/RestProxy.js | 44++++++++++++++++++++++++++++++++++++++++++--
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Data/Schema.js | 0
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Util/JsonWithAssociationsWriter.js | 1+
Msrc/PartKeepr/SiPrefixBundle/Controller/DefaultController.php | 4++--
Msrc/PartKeepr/SiPrefixBundle/Entity/SiPrefix.php | 9+++++----
Msrc/PartKeepr/UnitBundle/Controller/DefaultController.php | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/PartKeepr/UnitBundle/Entity/Unit.php | 58++++++++++------------------------------------------------
13 files changed, 273 insertions(+), 76 deletions(-)

diff --git a/src/PartKeepr/DoctrineReflectionBundle/Controller/DoctrineRESTQueryController.php b/src/PartKeepr/DoctrineReflectionBundle/Controller/DoctrineRESTQueryController.php @@ -4,14 +4,20 @@ namespace PartKeepr\DoctrineReflectionBundle\Controller; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\ORM\EntityManager; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Tools\Pagination\Paginator; use FOS\RestBundle\Controller\Annotations\QueryParam; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\Request\ParamFetcher; +use FOS\RestBundle\View\View; +use JMS\Serializer\Serializer; use PartKeepr\DoctrineReflectionBundle\Filter\Filter; use PartKeepr\DoctrineReflectionBundle\Sorter\Sorter; -use PartKeepr\PartKeepr; +use PartKeepr\Manager\Exceptions\EntityInUseException; +use PartKeepr\Util\BaseEntity; +use Symfony\Component\HttpFoundation\Request; class DoctrineRESTQueryController extends FOSRestController { @@ -53,7 +59,7 @@ class DoctrineRESTQueryController extends FOSRestController * @QueryParam(name="sort", default=null) * @QueryParam(name="filter", default=null) */ - public function getQueryResponseAction (ParamFetcher $paramFetcher) { + public function listAction (ParamFetcher $paramFetcher) { $qb = $this->getQueryBuilder(); $qb->select($this->getQueryAlias())->from($this->getTargetEntity(), $this->getQueryAlias()); @@ -66,10 +72,14 @@ class DoctrineRESTQueryController extends FOSRestController $paginator = new Paginator($query); - return array( - "totalCount" => count($paginator), - "data" => $paginator->getIterator()->getArrayCopy() - ); + $data = array(); + $data["data"] = $paginator->getIterator()->getArrayCopy(); + $data["_totalCount"] = count($paginator); + + $view = View::create() + ->setStatusCode(200) + ->setData($data); + return $view; } /** @@ -243,7 +253,7 @@ class DoctrineRESTQueryController extends FOSRestController */ protected function getQueryBuilder() { - return PartKeepr::getEM()->createQueryBuilder(); + return $this->getEM()->createQueryBuilder(); } /** @@ -265,4 +275,69 @@ class DoctrineRESTQueryController extends FOSRestController return $this->queryAliases[$entity]; } + + public function getAction ($id) { + return array("data" => $this->getEM()->find($this->getTargetEntity(), $id)); + } + + /** + * Returns the entity manager + * @return EntityManager + */ + public function getEM () { + return $this->get("doctrine")->getEntityManager(); + } + + public function putAction (Request $request, $id) { + $serializer = $this->get("jms_serializer"); + + /** + * @var $serializer Serializer + */ + $entity = $this->getEM()->find($this->getTargetEntity(), $id); + + $context = new \JMS\Serializer\DeserializationContext(); + $context->attributes->set('target', $entity); + + $updated = $serializer->deserialize($request->getContent(), $this->getTargetEntity(), 'json', $context); + + $this->getEM()->persist($updated); + $this->getEM()->flush(); + + return $entity; + } + + public function postAction (Request $request) { + $serializer = $this->get("jms_serializer"); + + /** + * @var $serializer Serializer + */ + + $newEntity = $serializer->deserialize($request->getContent(), $this->getTargetEntity(), 'json'); + + $this->getEM()->persist($newEntity); + $this->getEM()->flush(); + + return $newEntity; + } + + public function deleteAction ($id) { + /** @var $entity BaseEntity */ + $entity = $this->getEM()->find($this->getTargetEntity(), $id); + + try { + $this->getEM()->remove($entity); + $this->getEM()->flush(); + } catch (ForeignKeyConstraintViolationException $e) { + throw new EntityInUseException($entity); + } + + $view = View::create() + ->setStatusCode(200) + ->setData(array("success" => true)); + + return $view; + } + } \ No newline at end of file diff --git a/src/PartKeepr/DoctrineReflectionBundle/Exception/ExceptionWrapperHandler.php b/src/PartKeepr/DoctrineReflectionBundle/Exception/ExceptionWrapperHandler.php @@ -0,0 +1,28 @@ +<?php +namespace PartKeepr\DoctrineReflectionBundle\Exception; + +use FOS\RestBundle\View\ExceptionWrapperHandlerInterface; +use Symfony\Component\Debug\Exception\FlattenException; + +class ExceptionWrapperHandler implements ExceptionWrapperHandlerInterface +{ + /** + * @param array $data + * + * @return array + */ + public function wrap($data) + { + // we get the original exception + /** @var $exception FlattenException */ + $exception = $data['exception']; + + // return the array + return array( + 'success' => false, + 'exception' => array( + "message" => $exception->getMessage() + ) + ); + } +}+ \ No newline at end of file diff --git a/src/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig b/src/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig @@ -4,13 +4,22 @@ Ext.define('{{ className }}', { fields: [ {% for field in fields %} - { - name: '{{ field.name }}', - type: '{{ field.type }}' - }{% if not loop.last %},{% endif %} + { name: '{{ field.name }}'{% if field.type%}, type: '{{ field.type }}'{% endif %}}{% if not loop.last %},{% endif %} {% endfor %} ], + {% if associations.MANY_TO_MANY|length > 0 %} + manyToMany: { + {% for association in associations.MANY_TO_MANY %} + {{ association.name }}: { + type: '{{ association.target }}', + role: '{{ association.name }}', + field: 'id', + right: true + } {% if not loop.last %},{% endif %} + {% endfor %} + }, + {% endif %} proxy: { type: "PartKeeprREST", diff --git a/src/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php b/src/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php @@ -5,6 +5,7 @@ use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\Common\Annotations\Reader; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Symfony\Component\Templating\EngineInterface; class ReflectionService { @@ -48,19 +49,40 @@ class ReflectionService { $fields = $cm->getFieldNames(); - $mappings = array(); + $fieldMappings = array(); foreach ($fields as $field) { $currentMapping = $cm->getFieldMapping($field); - $mappings[] = array( + $fieldMappings[] = array( "name" => $currentMapping["fieldName"], "type" => $this->getExtJSFieldMapping($currentMapping["type"]), ); } + $associations = $cm->getAssociationMappings(); + + $associationMappings = array(); + + foreach ($associations as $association) { + $associationType = $association["type"]; + switch ($association["type"]) { + case ClassMetadataInfo::MANY_TO_MANY: + $associationType = "MANY_TO_MANY"; + break; + //default: +// die("Unknown association ".$association["type"]); + } + + $associationMappings[$associationType][] = array( + "name" => $association["fieldName"], + "target" => $this->convertPHPToExtJSClassName($association["targetEntity"]), + ); + } + $renderParams = array( - "fields" => $mappings, + "fields" => $fieldMappings, + "associations" => $associationMappings, "className" => $this->convertPHPToExtJSClassName($entity), ); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/Editor.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/Editor.js @@ -1,3 +1,4 @@ + Ext.define('PartKeepr.Editor', { extend: 'Ext.form.Panel', alias: 'widget.Editor', diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js @@ -111,7 +111,10 @@ Ext.define('PartKeepr.EditorComponent', { editor = this.createEditor(record.get("name")); editor.editItem(record); this.editorTabPanel.add(editor).show(); - } + }, + failure: function(record, operation) { + console.log(operation); + } }); }, findEditor: function (id) { @@ -165,7 +168,7 @@ Ext.define('PartKeepr.EditorComponent', { this.editorTabPanel.remove(editor); } - r.destroy(); + r.erase(); this.store.load(); }, // Creates a store. To be called from child's initComponent diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Data/RestProxy.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Data/RestProxy.js @@ -5,11 +5,51 @@ Ext.define("PartKeepr.data.RestProxy", { reader: { type: 'json', rootProperty: 'data', - totalProperty: 'totalCount' + totalProperty: '_totalCount' }, + writer: { + type: 'jsonwithassociations' + }, + appendId: true, constructor: function (config) { config.url = PartKeepr.getBasePath() + config.url; this.callParent(arguments); - } + }, + listeners: { + exception: function (proxy, response, operation) { + try { + var data = Ext.decode(response.responseText); + + var requestData = {}; + requestData.method = operation.request.method; + requestData.headers = operation.request.headers; + requestData.jsonData = operation.request.jsonData; + + request = { + request: Ext.encode(requestData), + response: response.responseText + }; + + PartKeepr.ExceptionWindow.showException(data.exception, request); + } catch (ex) { + var exception = { + message: i18n("Critical Error"), + detail: i18n("The server returned a response which we were not able to interpret.") + }; + + + requestData.method = operation.request.method; + requestData.headers = operation.request.headers; + requestData.jsonData = operation.request.jsonData; + + request = { + request: Ext.encode(requestData), + response: response.responseText + }; + + PartKeepr.ExceptionWindow.showException(exception, request); + } + } + }, }); \ No newline at end of file diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Data/Schema.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Data/Schema.js diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Util/JsonWithAssociationsWriter.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Util/JsonWithAssociationsWriter.js @@ -6,6 +6,7 @@ Ext.define("PartKeepr.JsonWithAssociations", { * @cfg {Array} associations Which associations to include. */ associations: [], + writeRecordId: false, getRecordData: function(record) { var me = this, i, key, subStore, diff --git a/src/PartKeepr/SiPrefixBundle/Controller/DefaultController.php b/src/PartKeepr/SiPrefixBundle/Controller/DefaultController.php @@ -22,10 +22,10 @@ class DefaultController extends DoctrineRESTQueryController * * {@inheritdoc} */ - public function getQueryResponseAction(ParamFetcher $paramFetcher) + public function listAction(ParamFetcher $paramFetcher) { $this->setTargetEntity("PartKeepr\\SiPrefixBundle\\Entity\\SiPrefix"); - return parent::getQueryResponseAction($paramFetcher); + return parent::listAction($paramFetcher); } } \ No newline at end of file diff --git a/src/PartKeepr/SiPrefixBundle/Entity/SiPrefix.php b/src/PartKeepr/SiPrefixBundle/Entity/SiPrefix.php @@ -1,6 +1,7 @@ <?php namespace PartKeepr\SiPrefixBundle\Entity; +use JMS\Serializer\Annotation\ReadOnly; use PartKeepr\Util\BaseEntity, Doctrine\ORM\Mapping as ORM, Symfony\Component\Validator\Constraints as Assert, @@ -23,7 +24,7 @@ class SiPrefix extends BaseEntity * * @Assert\Type(type="string") * @Assert\NotBlank(message="siprefix.prefix.not_blank") - * + * @ReadOnly() * @var string */ private $prefix; @@ -35,7 +36,7 @@ class SiPrefix extends BaseEntity * * @Assert\Type(type="string") * @Assert\NotBlank(message="siprefix.symbol.not_blank") - * + * @ReadOnly() * @var string */ private $symbol; @@ -45,7 +46,7 @@ class SiPrefix extends BaseEntity * * @ORM\Column(type="integer") * @Assert\Type(type="integer") - * + * @ReadOnly() * @var int */ private $exponent; @@ -55,7 +56,7 @@ class SiPrefix extends BaseEntity * * @ORM\Column(type="integer") * @Assert\Type(type="integer") - * + * @ReadOnly() * @var int */ private $base; diff --git a/src/PartKeepr/UnitBundle/Controller/DefaultController.php b/src/PartKeepr/UnitBundle/Controller/DefaultController.php @@ -8,11 +8,14 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration as Routing; use JMS\Serializer\Annotation as JMS; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use FOS\RestBundle\Controller\Annotations\View; +use Symfony\Component\HttpFoundation\Request; class DefaultController extends DoctrineRESTQueryController { + protected $targetEntity = "PartKeepr\\UnitBundle\\Entity\\Unit"; + /** - * Retrieves SI Prefixes in the database + * Retrieves units * * @Routing\Route("/unit", defaults={"method" = "get","_format" = "json"}) * @Routing\Method({"GET"}) @@ -22,10 +25,61 @@ class DefaultController extends DoctrineRESTQueryController * * {@inheritdoc} */ - public function getQueryResponseAction(ParamFetcher $paramFetcher) + public function listAction(ParamFetcher $paramFetcher) + { + return parent::listAction($paramFetcher); + } + + /** + * Retrieves a single unit + * + * @Routing\Route("/unit/{id}", defaults={"method" = "get","_format" = "json"}) + * @Routing\Method({"GET"}) + * @ApiDoc() + * @View() + */ + public function getAction ($id) { + return parent::getAction($id); + } + + /** + * Saves a single unit + * + * @Routing\Route("/unit/{id}", defaults={"_format" = "json"}) + * @Routing\Method({"PUT"}) + * @ApiDoc() + * + * @View() + */ + public function putAction(Request $request, $id) { - $this->setTargetEntity("PartKeepr\\UnitBundle\\Entity\\Unit"); + return parent::putAction($request, $id); + } - return parent::getQueryResponseAction($paramFetcher); + /** + * Saves a single unit + * + * @Routing\Route("/unit", defaults={"_format" = "json"}) + * @Routing\Method({"POST"}) + * @ApiDoc() + * + * @View() + */ + public function postAction(Request $request) + { + return parent::postAction($request); + } + + /** + * Deletes a single unit + * + * @Routing\Route("/unit/{id}", defaults={"_format" = "json"}) + * @Routing\Method({"DELETE"}) + * @ApiDoc() + * + * @View() + */ + public function deleteAction ($id) { + return parent::deleteAction($id); } } \ No newline at end of file diff --git a/src/PartKeepr/UnitBundle/Entity/Unit.php b/src/PartKeepr/UnitBundle/Entity/Unit.php @@ -1,14 +1,12 @@ <?php namespace PartKeepr\UnitBundle\Entity; -use PartKeepr\Unit\ArrayCollection; -use PartKeepr\Util\Deserializable, - PartKeepr\Util\Serializable, - PartKeepr\Util\BaseEntity, - PartKeepr\PartKeepr, - PartKeepr\Util\Exceptions\OutOfRangeException, +use Doctrine\Common\Collections\ArrayCollection; +use JMS\Serializer\Annotation\ExclusionPolicy; +use PartKeepr\Util\BaseEntity, PartKeepr\SiPrefixBundle\Entity\SiPrefix, Doctrine\ORM\Mapping as ORM, + JMS\Serializer\Annotation\Type, PartKeepr\DoctrineReflectionBundle\Annotation\TargetService; @@ -17,11 +15,14 @@ use PartKeepr\Util\Deserializable, * * @ORM\Entity * @TargetService(uri="/unit") + * @ExclusionPolicy("none") **/ -class Unit extends BaseEntity implements Serializable, Deserializable { +class Unit extends BaseEntity { /** * The name of the unit (e.g. Volts, Ampere, Farad, Metres) * @ORM\Column(type="string") + * @Type("string") + * * @var string */ private $name; @@ -29,6 +30,7 @@ class Unit extends BaseEntity implements Serializable, Deserializable { /** * The symbol of the unit (e.g. V, A, F, m) * @ORM\Column(type="string") + * @Type("string") * @var string */ private $symbol; @@ -40,6 +42,7 @@ class Unit extends BaseEntity implements Serializable, Deserializable { * joinColumns={@ORM\JoinColumn(name="unit_id", referencedColumnName="id")}, * inverseJoinColumns={@ORM\JoinColumn(name="siprefix_id", referencedColumnName="id")} * ) + * @Type("ArrayCollection<PartKeepr\SiPrefixBundle\Entity\SiPrefix>") * @var ArrayCollection */ private $prefixes; @@ -90,45 +93,4 @@ class Unit extends BaseEntity implements Serializable, Deserializable { public function getPrefixes () { return $this->prefixes; } - - /** - * Serializes the user object and returns it as array, suitable - * to process via json_encode. - * @param none - * @return array An array containing the object information - */ - public function serialize () { - return array( - "id" => $this->getId(), - "name" => $this->getName(), - "symbol" => $this->getSymbol(), - "prefixes" => $this->serializeChildren($this->getPrefixes()) - ); - } - - /** - * Deserializes the unit - * @param array $parameters The array with the parameters to set - */ - public function deserialize (array $parameters) { - foreach ($parameters as $key => $value) { - switch ($key) { - case "name": - $this->setName($value); - break; - case "symbol": - $this->setSymbol($value); - break; - case "prefixes": - $prefixes = $this->getPrefixes(); - $prefixes->clear(); - - foreach ($value as $prefix) { - $prefix = SiPrefix::loadById($prefix["id"]); - $prefixes->add($prefix); - } - break; - } - } - } } \ No newline at end of file