Extbase ObjectStorage: herausfinden, ob DomainModel in ObjectStorage existiert

  • sunixzs sunixzs
    R2-D2
    0 x
    123 Beiträge
    2 Hilfreiche Beiträge
    20. 04. 2013, 17:44

    Hallo liebes Forum,

    In einem Model (Parent) habe ich eine mm-Relation zu einem anderen Model (Child). Nun muss ich herausfinden, ob ein bestimmtes Child-Objekt als Parameter des Parent-Objekts existiert. Bei Arrays wäre es einfach die Methode in_array(). Nun wäre die Frage, ob es dergleichen für ein ObjectStorage gibt - in_object() oder in_storageObject() sozusagen.

    Hier meine Lösungsansätze:

    LÖSUNG 1:
    Diese funktioniert gut, wenn man nur wenige Objekte hat, über die man iterieren muss - aber wer weiß schon, wieviele Objekte es werden?

    1. public function hasChild($theChildToDetect) {
    2. if (!$theChildToDetect) {
    3. return false;
    4. }
    5.  
    6. foreach ($this->getChildren() as $child) {
    7. if ($child->getUid() == $theChildToDetect->getUid()) {
    8. return true;
    9. }
    10. }
    11. return false;
    12. }

    LÖSUNG 2:
    Methode der Wahl, wenn es sehr viele Child-Objekte gibt: befrage die Datenbank. Hier muss noch nichtmal ein ObjectStorage für alle Child-Objekte geschaffen werden. Es kann aber sehr auf die Datenbank gehen, wenn diese Methode oft aufgerufen werden muss.

    1. public function hasChild($theChildToDetect) {
    2. if (!$theChildToDetect) {
    3. return false;
    4. }
    5.  
    6. $parentRepository = $this->objectManager->get('VENDOR\\ExtKey\\Domain\\Repository\\ParentRepository');
    7. return ($parentRepository->findByUidContainsChild($this->getUid(), $theChildToDetect) ? true : false;
    8. }

    Lösung 3:
    Im ObjectStorage wird mit spl_object_hash() ein Hash gebildet, der als Key im Storage benutzt wird. Diesen kann man mit der Storage-Methode offsetExists() abfragen.

    1. public function hasChild($theChildToDetect) {
    2. if (!$theChildToDetect) {
    3. return false;
    4. }
    5.  
    6. return $this->getChildren()->offsetExists($theChildToDetect);
    7. }

    Welches ist denn die Methode, die verwendet werden sollte?

    Vielen Dank für Hinweise, die meine Unsicherheiten beseitigen ;)

    LG sun


  • 1
  • kainobi kainobi
    Jedi-Ritter
    0 x
    141 Beiträge
    0 Hilfreiche Beiträge
    21. 04. 2013, 09:24

    Eine Methode, welche über den spl_object_hash geht, bietet der ObjectStorage bereits. Sie heisst contains(). Schwer zu sagen, ab welcher Anzahl die Methode Performance-Probleme verursacht. Anfragen an die Persistenz-Schicht gehören eigentlich nicht ins Model. Darum würde ich, falls du es über das Repository lösen willst, dies in einer Service-Klasse implementieren.

  • LuP LuP
    Jedi-Meister
    0 x
    496 Beiträge
    1 Hilfreiche Beiträge
    21. 04. 2013, 10:41

    Hallo,

    ich persönlich setze für Property vom Typ ObjectStorage bisher auf Lösung 3. Bei Verwendung von lazy wird sie ebenfalls unterstützt, was der Performance nur zu Gute kommen kann. Ob es nun über contains() oder direkt per offsetExists() geht, müsste das gleiche heraus kommen, denn contains() implementiert auch offsetExists().

    VG,
    LuP

  • sunixzs sunixzs
    R2-D2
    0 x
    123 Beiträge
    2 Hilfreiche Beiträge
    22. 04. 2013, 09:43

    Moin,

    danke für's Feedback - das geht ja eindeutig Richtung ObjectStorage-Methode.

    Am besten mache ich es etwas konkreter, wie die Struktur ist:
    Es gibt ein DomainModel Category mit der Relation zum DomainModel Entry. Allgemein darf jeder FrontendUser die Entry im Category sehen. Ausgewählte FrontendUser - nennen wir sie Member der Category - dürfen aber mehr: Entry der Category hinzufügen.

    Dadurch, dass ich noch mehr über den aktuellen FrontendUser wissen muss - z.B. Zugriffsrechte für andere Objekte setzen und Benutzername ausgeben, erstelle ich in der initializeAction() des Controllers mit Hilfe einer Service-Klasse ein $GLOBALS-Object des aktuellen FrontendUser, um diesen überall zur Verfügung zu haben. Category wäre im obigen Beispiel der Parent und FrontendUser der Child.

    Da der FrontendUser global ist, könnte ich in diesem Fall auf den Parameter im Getter verzichten - da ist schonmal etwas gewonnen:

    1. public function isMember() {
    2. if (!$GLOBALS['theCurrentFrontendUser']) {
    3. return false;
    4. }
    5. return $this->getMember()->offsetExists($GLOBALS['theCurrentFrontendUser']);
    6. }

    Nun noch konkreter:
    30 auszugebende Category im View sind denkbar. Mal angenommen, es gibt ca. 200 FrontendUser, von denen 50 zum Kern gehören und Member der Category sind.

    - Bei der foreach-Schleife wären das im Grenzfall 30x50 = 1500 Iterationen über Domain-Objekte im ObjectStorage nur bei der Ausgabe der Category. Alle Member-Objekte müssen in der Persistence-Schicht existieren.
    - Bei der Befrage-die-Datenbank-Methode wären es 30 zusätzliche Zugriffe auf die Datenbank.
    - Mit der bevorzugten offsetExists()- oder contains()-Methode müssen auch alle Member-Objekte aus der Datenbank geholt werden. Der wesentliche Vorteil ist hier, dass nur einmal über alle Member-Objekte iteriert wird, um die Domain-Objekte zu erstellen (afaik).

    Da kommt mir gerade eine Idee:
    Da Category und Member eine mm-Relation darstellt, kann ich sie auch bidirektional anlegen und von Seitens des Member aus abfragen. Das dürfte die Menge der Domain-Objekte deutlich verringern, da eh alle Category ausgegeben werden und der Member nur in einigen von denen vertreten ist. Es muss nur der aktuelle FrontendUser vorhanden sein und nicht alle FrontendUser, die in einer der Category Member sind.

    Hier die Implementierung der bidirektionalen mm-Relation, da diese nicht so bekannt sein dürfte:

    1. #
    2. # TABLE STRUCTURE FOR TABLE 'tx_exttitle_category_frontenduser_mm'
    3. #
    4. CREATE TABLE tx_exttitle_category_frontenduser_mm (
    5. uid_local INT(11) UNSIGNED DEFAULT '0' NOT NULL,
    6. uid_foreign INT(11) UNSIGNED DEFAULT '0' NOT NULL,
    7. sorting INT(11) UNSIGNED DEFAULT '0' NOT NULL,
    8. sorting_foreign INT(11) UNSIGNED DEFAULT '0' NOT NULL,
    9. category INT(11) UNSIGNED DEFAULT '0' NOT NULL,
    10.  
    11. KEY uid_local (uid_local),
    12. KEY uid_foreign (uid_foreign)
    13. );

    TCA Category:

    1. 'member' => array(
    2. 'exclude' => 0,
    3. 'label' => 'Member',
    4. 'config' => array(
    5. 'type' => 'group',
    6. 'internal_type' => 'db',
    7. 'foreign_table' => 'fe_users',
    8. 'allowed' => 'fe_users',
    9. 'MM' => 'tx_exttitle_category_frontenduser_mm',
    10. 'MM_opposite_field' => 'category',
    11. 'maxitems' => 99999,
    12. 'show_thumbs' => 1,
    13. 'size' => 10,
    14. ),
    15. ),

    TCA Member bzw. FrontendUser

    1. 'category' => array(
    2. 'exclude' => 0,
    3. 'label' => 'Category',
    4. 'config' => array(
    5. 'type' => 'group',
    6. 'internal_type' => 'db',
    7. 'foreign_table' => 'tx_exttitle_domain_model_category',
    8. 'allowed' => 'tx_exttitle_domain_model_category',
    9. 'MM' => 'tx_exttitle_category_frontenduser_mm',
    10. 'size' => 10,
    11. 'maxitems' => 99999,
    12. ),
    13. ),

    Die Deklaration des ObjectStorage inkl. Getter und Setter müssen dann ganz normal in beiden DomainModels vorhanden sein (im CategoryModel $member und im FrontendUserModel $category).

    Die isMember()-Methode verschiebt sich jetzt in das FrontendUser-Model (wobei ich hier nicht mehr mit global arbeiten kann und dem Getter einen Parameter mitgeben müsste. Deswegen kann ich diesen Getter auch gleich weglassen und in einen ViewHelper verschieben):

    1. public function isMemberOfCategory($categoryToDetect) {
    2. if (!$categoryToDetect) {
    3. return false;
    4. }
    5. return $this->getCategory()->offsetExists($categoryToDetect);
    6. }

    Eine Abfrage im View müsste durch einen ViewHelper erledigt werden:

    1. <f:if condition="{tx:categoryHasMember(category:categoryInForLoop, member:currentFrontendUser)}"></f:if>

    CategoryHasMemberViewHelper:

    1. public function render($category, $member) {
    2. if (!$member) {
    3. return false;
    4. }
    5. return $member->getCategory()->getCategory()->offsetExists($category);
    6. }

    Falls ich mal irgendwann konkreteres weiß bzgl. Performance und Speicherbedarf, geb' ich bescheid.

    Vielen Dank für euer Gehör und sonnige Grüße
    sun

  • 1