partkeepr

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

commit 2159771cdfb1884701df82a6b1b1d6450039e353
parent 2a5f1bbaf0da65bb0a710790bc2c2f19fe164f1b
Author: Felicitus <felicitus@felicitus.org>
Date:   Tue, 21 Jun 2011 12:56:02 +0200

Refactored the way we push information back from the frontend.

Changes:
- We can now use the deserialize interface to deserialize information
- We support nested records on the JS side (via JsonWithAssociationsWriter)
- Several methods added to make handling associations easier

Diffstat:
Mfrontend/file.php | 38++++++++++++++++++++++----------------
Mfrontend/image.php | 41+++++++++++++++++++++++------------------
Mfrontend/js/Components/Editor/Editor.js | 30------------------------------
Mfrontend/js/Components/Footprint/FootprintEditor.js | 6+-----
Mfrontend/js/Components/Footprint/FootprintTree.js | 13+++++++++----
Mfrontend/js/Components/Widgets/AttachmentGrid.js | 7+++----
Mfrontend/js/Models/FootprintAttachment.js | 5++---
Mfrontend/js/Util/JsonWithAssociationsWriter.js | 13++++---------
Msrc/de/RaumZeitLabor/PartKeepr/Footprint/Footprint.php | 55++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/de/RaumZeitLabor/PartKeepr/Footprint/FootprintAttachment.php | 24+++++++++++++++++++++++-
Msrc/de/RaumZeitLabor/PartKeepr/Footprint/FootprintService.php | 49++++++-------------------------------------------
Msrc/de/RaumZeitLabor/PartKeepr/Image/Image.php | 16++++++++++++++++
Msrc/de/RaumZeitLabor/PartKeepr/UploadedFile/UploadedFile.php | 36++++++++++++++++++++++++++++++++++++
Msrc/de/RaumZeitLabor/PartKeepr/Util/BaseEntity.php | 69++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
14 files changed, 267 insertions(+), 135 deletions(-)

diff --git a/frontend/file.php b/frontend/file.php @@ -19,26 +19,32 @@ PartKeepr::initialize(""); $type = $_REQUEST["type"]; $id = $_REQUEST["id"]; -try { - switch ($type) { - case "PartAttachment": - $file = PartAttachment::loadById($id); - break; - case "FootprintAttachment": - case "PartKeepr.FootprintAttachment": - $file = FootprintAttachment::loadById($id); - break; - default: - $file = null; - // Add default image? +if (substr($id, 0, 4) === "TMP:") { + $tmpFileId = str_replace("TMP:", "", $id); + $file = TempUploadedFile::loadById($tmpFileId); +} else { + try { + switch ($type) { + case "PartAttachment": + $file = PartAttachment::loadById($id); + break; + case "FootprintAttachment": + case "PartKeepr.FootprintAttachment": + $file = FootprintAttachment::loadById($id); + break; + default: + $file = null; + // Add default image? + } + + } catch (\Exception $e) { + $file = null; + // Something bad happened } - -} catch (\Exception $e) { - $file = null; - // Something bad happened } if ($file == null) { + // Could not find the image, but maybe we want a temporary image? if (array_key_exists("tmpId", $_REQUEST)) { $file = TempUploadedFile::loadById($_REQUEST["tmpId"]); diff --git a/frontend/image.php b/frontend/image.php @@ -20,25 +20,30 @@ PartKeepr::initialize(""); $type = $_REQUEST["type"]; $id = $_REQUEST["id"]; -try { - switch ($type) { - case Image::IMAGE_ICLOGO: - $image = ManufacturerICLogo::loadById($id); - break; - case Image::IMAGE_FOOTPRINT: - $image = FootprintImage::loadById($id); - break; - case Image::IMAGE_PART: - $image = PartImage::loadById($id); - break; - default: - $image = null; - // Add default image? +if (substr($id, 0, 4) === "TMP:") { + $tmpImageId = str_replace("TMP:", "", $id); + $image = TempImage::loadById($tmpImageId); +} else { + try { + switch ($type) { + case Image::IMAGE_ICLOGO: + $image = ManufacturerICLogo::loadById($id); + break; + case Image::IMAGE_FOOTPRINT: + $image = FootprintImage::loadById($id); + break; + case Image::IMAGE_PART: + $image = PartImage::loadById($id); + break; + default: + $image = null; + // Add default image? + } + + } catch (\Exception $e) { + $image = null; + // Something bad happened } - -} catch (\Exception $e) { - $image = null; - // Something bad happened } if ($image == null) { diff --git a/frontend/js/Components/Editor/Editor.js b/frontend/js/Components/Editor/Editor.js @@ -92,40 +92,10 @@ Ext.define('PartKeepr.Editor', { }, onItemSave: function () { this.getForm().updateRecord(this.record); - this.record.save({ callback: this._onSave, scope: this }); - -/* - this.store.load({ - scope : this, - callback: function(records, operation, success) { - for (i=0;i<records.length;i++) { - if (records[i].get("id") == this.record.get("id")) { - this.editItem(records[i]); - break; - } - } - }}); - }, - scope: this - }); - } else { - this.getForm().updateRecord(this.record); - - if ((this.record.dirty && !this.record.store) || this.syncDirect == true) { - this.record.save(); - } else { - // Sync via the store - this.store.sync(); - } - - this.fireEvent("itemSaved", this.record); - } - }*/ - }, _onSave: function (record) { this.record = record; diff --git a/frontend/js/Components/Footprint/FootprintEditor.js b/frontend/js/Components/Footprint/FootprintEditor.js @@ -57,11 +57,7 @@ Ext.define('PartKeepr.FootprintEditor', { this.callParent(); }, _onItemSaved: function (record) { - this.attachmentGrid.store.each(function (record) { - record.set("footprint_id", this.record.get("id")); - }, this); - - this.attachmentGrid.store.sync(); + this.attachmentGrid.bindStore(record.attachments()); }, onEditStart: function () { var store = this.record.attachments(); diff --git a/frontend/js/Components/Footprint/FootprintTree.js b/frontend/js/Components/Footprint/FootprintTree.js @@ -27,12 +27,17 @@ Ext.define("PartKeepr.FootprintTree", { if (r && !r.get("footprintId")) { this.fireEvent("itemAdd", { category: r.get("id") }); } else { - /* Try to find the category's parent id */ - if (r.parentNode && !r.parentNode.get("footprintId")) { - this.fireEvent("itemAdd", { category: r.parentNode.get("id") }); + if (!r) { + this.fireEvent("itemAdd", this.getRootNode().get("id")); } else { - this.fireEvent("itemAdd"); + /* Try to find the category's parent id */ + if (r.parentNode && !r.parentNode.get("footprintId")) { + this.fireEvent("itemAdd", { category: r.parentNode.get("id") }); + } else { + this.fireEvent("itemAdd", this.getRootNode().get("id")); + } } + } }, this) diff --git a/frontend/js/Components/Widgets/AttachmentGrid.js b/frontend/js/Components/Widgets/AttachmentGrid.js @@ -103,13 +103,12 @@ Ext.define('PartKeepr.AttachmentGrid', { onFileUploaded: function (response) { this.editing.cancelEdit(); - this.getStore().add({ - id: 0, - tmp_id: response.id, + this.store.insert(this.store.getCount(), Ext.create(this.model, { + id: "TMP:"+response.id, extension: response.extension, size: response.size, originalFilename: response.originalFilename - }); + })); }, onDeleteClick: function () { diff --git a/frontend/js/Models/FootprintAttachment.js b/frontend/js/Models/FootprintAttachment.js @@ -1,14 +1,13 @@ Ext.define("PartKeepr.FootprintAttachment", { extend: "Ext.data.Model", fields: [ - { id: 'id', name: 'id', type: 'int' }, + { id: 'id', name: 'id', type: 'string' }, { name: 'originalFilename', type: 'string' }, { name: 'footprint_id', type: 'int' }, { name: 'mimetype', type: 'string' }, { name: 'extension', type: 'string' }, { name: 'description', type: 'string' }, - { name: 'size', type: 'string' }, - { name: 'tmp_id', type: 'int' } + { name: 'size', type: 'string' } ], belongsTo: { type: 'belongsTo', model: 'PartKeepr.Footprint', primaryKey: 'id', foreignKey: 'footprint_id'}, proxy: PartKeepr.getRESTProxy("FootprintAttachment") diff --git a/frontend/js/Util/JsonWithAssociationsWriter.js b/frontend/js/Util/JsonWithAssociationsWriter.js @@ -11,15 +11,10 @@ Ext.define("PartKeepr.JsonWithAssociations", { var me = this, i, key, subStore, data = me.callParent(arguments); - for (i=0;i<me.associations.length;i++) { - key = me.associations[i]; - data[key] = []; - subStore = record[record.associations.getByKey(key).storeName]; - subStore.each(function (subRecord) { - data[key].push(ReConsole.writer.JsonWithAssociations.superclass.getRecordData.call(this, subRecord)); - }, me); - } - + var storeName; + + Ext.apply(data, record.getAssociatedData()); + return data; } }); \ No newline at end of file diff --git a/src/de/RaumZeitLabor/PartKeepr/Footprint/Footprint.php b/src/de/RaumZeitLabor/PartKeepr/Footprint/Footprint.php @@ -1,5 +1,7 @@ <?php namespace de\RaumZeitLabor\PartKeepr\Footprint; +use de\RaumZeitLabor\PartKeepr\Util\Deserializable; + use de\RaumZeitLabor\PartKeepr\Util\Serializable; use de\RaumZeitLabor\PartKeepr\FootprintCategory\FootprintCategory; use de\RaumZeitLabor\PartKeepr\Util\BaseEntity; @@ -8,7 +10,7 @@ declare(encoding = 'UTF-8'); /** @Entity */ -class Footprint extends BaseEntity implements Serializable { +class Footprint extends BaseEntity implements Serializable, Deserializable { /** * Holds the footprint name * @Column(length=64,unique=true) @@ -106,6 +108,7 @@ class Footprint extends BaseEntity implements Serializable { */ public function setImage (FootprintImage $image) { $this->image = $image; + $image->setFootprint($this); } /** @@ -138,4 +141,54 @@ class Footprint extends BaseEntity implements Serializable { "attachments" => $this->serializeChildren($this->getAttachments()) ); } + + /** + * Deserializes the footprint + * @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 "description": + $this->setDescription($value); + break; + case "image_id": + if ($value == "") { + echo "/** Breaking because of empty value */"; + break; } + + try { + $image = FootprintImage::loadById($value); + $this->setImage($image); + } catch (\Exception $e) { + if ($this->getImage()) { + // Image was not found, maybe a temporary image? + $this->getImage()->replaceFromTemporaryFile($value); + } else { + $image = FootprintImage::createFromTemporaryFile($value); + $this->setImage($image); + } + } + + break; + case "category": + try { + $category = FootprintCategory::loadById($value); + $this->setCategory($category); + } catch (\Exception $e) { + // Category was not found, do not change category. + } + break; + case "attachments": + $this->deserializeChildren($value, $this->getAttachments(), "de\RaumZeitLabor\PartKeepr\Footprint\FootprintAttachment"); + foreach ($this->getAttachments() as $attachment) { + $attachment->setFootprint($this); + } + break; + } + } + } } diff --git a/src/de/RaumZeitLabor/PartKeepr/Footprint/FootprintAttachment.php b/src/de/RaumZeitLabor/PartKeepr/Footprint/FootprintAttachment.php @@ -1,5 +1,7 @@ <?php namespace de\RaumZeitLabor\PartKeepr\Footprint; +use de\RaumZeitLabor\PartKeepr\Util\Deserializable; + use de\RaumZeitLabor\PartKeepr\Util\Serializable; declare(encoding = 'UTF-8'); @@ -10,7 +12,7 @@ use de\RaumZeitLabor\PartKeepr\UploadedFile\UploadedFile; * Holds a footprint attachment * @Entity **/ -class FootprintAttachment extends UploadedFile implements Serializable { +class FootprintAttachment extends UploadedFile implements Serializable, Deserializable { /** * The description of this attachment * @Column(type="text") @@ -79,4 +81,24 @@ class FootprintAttachment extends UploadedFile implements Serializable { "size" => $this->getSize(), "description" => $this->getDescription()); } + + /** + * Deserializes the footprint attachment + * @param array $parameters The array with the parameters to set + */ + public function deserialize (array $parameters) { + if (array_key_exists("id", $parameters)) { + if (substr($parameters["id"], 0, 4) === "TMP:") { + $this->replaceFromTemporaryFile($parameters["id"]); + } + } + + foreach ($parameters as $key => $value) { + switch ($key) { + case "description": + $this->setDescription($value); + break; + } + } + } } \ No newline at end of file diff --git a/src/de/RaumZeitLabor/PartKeepr/Footprint/FootprintService.php b/src/de/RaumZeitLabor/PartKeepr/Footprint/FootprintService.php @@ -44,28 +44,14 @@ class FootprintService extends Service implements RestfulService { public function create () { $this->requireParameter("name"); - $fp = FootprintManager::getInstance()->addFootprint($this->getParameter("name")); - $fp->setDescription($this->getParameter("description")); + $footprint = new Footprint(); + $footprint->deserialize($this->getParameters()); - if ($this->getParameter("image_id", false) !== false) { - if (strpos($this->getParameter("image_id"), "TMP:") !== false) { - $tmpImage = TempImage::loadById(str_replace("TMP:", "", $this->getParameter("image_id"))); - $image = new FootprintImage(); - $image->replace($tmpImage->getFilename()); - $image->setOriginalFilename($tmpImage->getOriginalFilename()); - - $fp->setImage($image); - $image->setFootprint($fp); - } - - } + PartKeepr::getEM()->persist($footprint); - try { - $footprintCategory = FootprintCategory::loadById($this->getParameter("category")); - $fp->setCategory($footprintCategory); - } catch (\Exception $e) {} + PartKeepr::getEM()->flush(); - return array("data" => $fp->serialize()); + return array("data" => $footprint->serialize()); } /** @@ -76,32 +62,9 @@ class FootprintService extends Service implements RestfulService { $this->requireParameter("id"); $this->requireParameter("name"); $footprint = Footprint::loadById($this->getParameter("id")); - $footprint->setName($this->getParameter("name")); - $footprint->setDescription($this->getParameter("description")); - if ($this->getParameter("image_id", false) !== false) { - if (strpos($this->getParameter("image_id"), "TMP:") !== false) { - $tmpImage = TempImage::loadById(str_replace("TMP:", "", $this->getParameter("image_id"))); - - $image= $footprint->getImage(); + $footprint->deserialize($this->getParameters()); - if ($image === null) { - $image = new FootprintImage(); - } - - $image->replace($tmpImage->getFilename()); - $image->setOriginalFilename($tmpImage->getOriginalFilename()); - $image->setFootprint($footprint); - - $footprint->setImage($image); - } - } - - try { - $footprintCategory = FootprintCategory::loadById($this->getParameter("category")); - $footprint->setCategory($footprintCategory); - } catch (\Exception $e) {} - PartKeepr::getEM()->flush(); return array("data" => $footprint->serialize()); diff --git a/src/de/RaumZeitLabor/PartKeepr/Image/Image.php b/src/de/RaumZeitLabor/PartKeepr/Image/Image.php @@ -5,6 +5,7 @@ declare(encoding = 'UTF-8'); use de\RaumZeitLabor\PartKeepr\PartKeepr, de\RaumZeitLabor\PartKeepr\UploadedFile\UploadedFile, de\RaumZeitLabor\PartKeepr\Util\Configuration, + de\RaumZeitLabor\PartKeepr\TempImage\TempImage, de\RaumZeitLabor\PartKeepr\Image\Exceptions\InvalidImageTypeException; /** @@ -231,5 +232,20 @@ abstract class Image extends UploadedFile { mkdir(Configuration::getOption("partkeepr.images.cache"), 0777, true); } } + + /** + * Replaces the file with a given temporary file. + * @param string $id The temporary id (prefixed with TMP:) + */ + public function replaceFromTemporaryFile ($id) { + if (substr($id, 0, 4) === "TMP:") { + $tmpFileId = str_replace("TMP:", "", $id); + $tmpFile = TempImage::loadById($tmpFileId); + + $this->replace($tmpFile->getFilename()); + $this->setOriginalFilename($tmpFile->getOriginalFilename()); + + } + } } diff --git a/src/de/RaumZeitLabor/PartKeepr/UploadedFile/UploadedFile.php b/src/de/RaumZeitLabor/PartKeepr/UploadedFile/UploadedFile.php @@ -5,6 +5,7 @@ use de\RaumZeitLabor\PartKeepr\Util\BaseEntity; declare(encoding = 'UTF-8'); use de\RaumZeitLabor\PartKeepr\PartKeepr, + de\RaumZeitLabor\PartKeepr\UploadedFile\TempUploadedFile, de\RaumZeitLabor\PartKeepr\Util\Configuration; /** @@ -229,4 +230,39 @@ abstract class UploadedFile extends BaseEntity { mkdir($this->getFilePath(), 0777, true); } } + + /** + * Creates a new entity from the given temporary id. + * + * @param string $id The temporary id (prefixed with TMP:) + * @return object a new instance of the file. + * @throws \Exception If the ID does not begin with TMP: + */ + public static function createFromTemporaryFile ($id) { + if (substr($id, 0, 4) === "TMP:") { + // It's a temporary file + $className = get_called_class(); + + $file = new $className(); + $file->replaceFromTemporaryFile($id); + return $file; + } else { + throw new \Exception("Given id $id is not a temporary file"); + } + } + + /** + * Replaces the file with a given temporary file. + * @param string $id The temporary id (prefixed with TMP:) + */ + public function replaceFromTemporaryFile ($id) { + if (substr($id, 0, 4) === "TMP:") { + $tmpFileId = str_replace("TMP:", "", $id); + $tmpFile = TempUploadedFile::loadById($tmpFileId); + + $this->replace($tmpFile->getFilename()); + $this->setOriginalFilename($tmpFile->getOriginalFilename()); + } + } + } diff --git a/src/de/RaumZeitLabor/PartKeepr/Util/BaseEntity.php b/src/de/RaumZeitLabor/PartKeepr/Util/BaseEntity.php @@ -22,6 +22,73 @@ class BaseEntity { return $this->id; } + /** + * Syncs a given collection with the entity's collection. + * + * This is used for 1:n or m:n relations, where we need to process inserts, updates and deletes for the records. + * + * @param array $sourceArray The array with all records which should be deserialized + * @param \Doctrine\Common\Collections\Collection $collection The collection which contains the existing records + * @param string $entityClass The class name which is used when a new entity needs to be created + */ + public function deserializeChildren (array $sourceArray, \Doctrine\Common\Collections\Collection $collection, $entityClass) { + $deletes = array(); + $inserts = array(); + + /* Round 1: Check if we've got a matching id in both lists. If yes, we know that the record + * should be updated. If no, the record should be appended */ + foreach ($sourceArray as $sourceItem) { + $bFound = false; + foreach ($collection as $item) { + if ($item->getId() == $sourceItem["id"]) { + // Directly update + $item->deserialize($sourceItem); + $bFound = true; + break; + } + } + + if (!$bFound) { + $inserts[] = $sourceItem; + } + } + + /* Round 2: Check for items which are in the collection but not in the sourceArray. */ + foreach ($collection as $targetItem) { + $bFound = false; + foreach ($sourceArray as $item) { + if ($targetItem->getId() == $item["id"]) { + $bFound = true; + break; + } + } + + if (!$bFound) { + $deletes[] = $targetItem; + } + } + + foreach ($inserts as $item) { + $class = new $entityClass; + $class->deserialize($item); + + $collection->add($class); + PartKeepr::getEM()->persist($class); + } + + /* Remove the to-be-deleted items from the collection. Note that we store the instance of the item, + * so we can simply use removeElement. + */ + foreach ($deletes as $item) { + $collection->removeElement($item); + PartKeepr::getEM()->remove($item); + } + } + + /** + * Serializes the children of a specific collection + * @param \Doctrine\Common\Collections\Collection $array The array holding BaseEntities to serialize + */ public function serializeChildren (\Doctrine\Common\Collections\Collection $array) { $aData = array(); $aData["totalCount"] = $array->count(); @@ -33,7 +100,7 @@ class BaseEntity { return array("response" => $aData); } - + /** * Loads the entity from the database. * @param integer $id The entity's id