Zum Inhalt springen

MM-Relation in Repository abfragen

Erstellt am 30. Mai 2011 · 12 Antworten · letzte Antwort am 30. Oktober 2016

Tags: Datenbank, Frage

oliver_g ·

Hi, ich hab momentan das gleiche Problem. Ich bekomme nur die Uids, nicht aber die ganzen Objekte. Ich denke, das ganze Repository-Konzept kann man vergessen bzgl. Relationen.

Außerdem ist das alles nicht besonders performant, da anstatt einer Verbundabfrage viele Einzelabfragen durchgeführt werden.

Ich denke, die "Abstraktion" von einem bestimmten SQL System bringt einem in der Praxis sowieso nichts, schon gar nicht bei Typo3. Daher werde ich die Relationssachen mit der $query->statement()-Methode umsetzen. Dann kann ich meinen Join schreiben, die Datenbank holt sich die Daten recht effizient.

Nicht umsonst hat Zend das Model in seinem Framework weggelassen, es bringt einfach nichts, von einer Datenbank zu abstrahieren und es gibt ca. 1 Million Möglichkeiten ein Modell umzusetzen. Das Repository-Konzept hat da wohl eher akademischen Nutzen.

Gruß

oliver_g ·

So, ich habe bei den zurückgelieferten Ids einfach noch mal ein "findByUid" durchgeführt, damit hab ich zumindestens die Datensätze. Ist zwar nicht performant aber dafür bin ich noch nicht auf SQL-Ebene hinangestiegen.

Also ungefähr so:

class.ObjectYRepository:

public function findByObjectX(Tx_Myext_Domain_Model_ObjectX $objX,) {
		
		
		$query = $this->createQuery();
		$query->getQuerySettings()->setRespectStoragePage(FALSE);
		$query->matching(
				$query->logicalAnd(
					$query->contains('objXLink', $objX),
					$query->equals('pid', $this->storagePid),
					
				)
		);
		
		$query->setLimit($this->maximumRecords);
		
		return $this->findByUid($query->execute());
	}

Gruß
Oliver

Unbekannter Benutzer ·

Hi,

ich bin schon wieder an einem ähnlichen Problem dran.

Um nur kurz auf meinen ersten Beitrag in diesem Thema zurückzukommen, der Fehler lag (aus heutiger Sicht) bei der Query Abfrage mit equals. Ich hätte da schon mit contains arbeiten sollen, dann wäre das Problem gelöst und ich müsste nicht den Umweg über for-Schleifen machen.

Sollte also so funktionieren:

<?php
/**
 * 
 * Returns all objects of this repository with matching category
 * 
 * @param Tx_MyExt_Domain_Model_Category $category
 * 
 * @return Tx_Extbase_Persistence_QueryResult
 */
public function findByCategory(Tx_MyExt_Domain_Model_Category $category) {
	$query = $this->createQuery();
	$query->matching($query->contains('categories', $category));
	$result = $query->execute();
	return $result;
}
?>

So, jetzt komm ich zu meinem neuen Problem:

die hier beschriebene Lösung funktioniert, wenn man nur auf eine einzige Beziehung prüft, d.h. $category darf nur ein Domain Model beinhalten. Sobald ich jedoch auf zwei oder mehr Kategorien prüfen möchten, funktioniert das nicht mehr.

Beispielanwendung:

Ich habe mehrere Bilder, welche jeweils mehreren Kategorien zugeordnet sind.
Nun möchte ich alle Bilder ausgeben, welche in Kategorie 1 und in Kategorie 2 sind.

Momentan mache ich das so, dass ich mir über das BildRepository alle Bilder hole und dann jedes einzelne Bild auf Kategoriezugehörigkeit prüfe. Das ist jedoch nicht sehr schön und es muss bestimmt besser gehen.

Das ganze sieht etwa so aus (Bild = Media):

<?php
public function listAction() {
	// gewünschte Kategorien holen
	$selectedCategories = $this->categoryRepository->findByUids(array(1, 6))->toArray();
	
	// Alle Bilder holen
	$medias = $this->mediaRepository->findAll()->toArray();
	
	// Jedes Bild einzeln prüfen
	foreach($medias as $key => $media) {
		foreach($selectedCategories as $category) {
			// Entfernt das Bild sobald mindestens eine Kategorie fehlt
			if(!$media->getCategory()->contains($category))
				unset($medias[$key]);
		}
	}
	$this->view->assign('categories', $selectedCategories);
	$this->view->assign('medias', $medias);
}
?>

Bei mehrerern tausend Bildern kann das ganz schön dauern. Also keine gute Lösung!

Wie kann ich schon im Repository auf mehrere MM-Relationen prüfen?

Danke für eure Hilfe 🙂

freshman17 ·

Du kannst die "statement"-Methode des Query-Objektes benutzen und damit auf den vollen Sprachumfang von SQL zurückgreifen.
Das Mapping wird für dich ebenfalls übernommen (auf jeden Fall ab extbase 1.3) aber nur für das korrespondente Model (in diesem Fall Tx_MyExt_Domain_Model_Category).

Einen Nachteil hat es und zwar wird die "statement"-Methode in FLOW3 nicht unterstützt.

class Tx_MyExt_Domain_Repository_CategoryRepository extends Tx_Extbase_Persistence_Repository {

	/**
	 * findImagesByFoo
	 *
	 * @return Tx_Extbase_Persistence_QueryResult
	 */
	public function findImagesByFoo() {
		$sql = 'SELECT DISTINCT category.* FROM tx_myext_domain_model_category AS category
				......
				WHERE image = ....';
		return $this->createQuery()->statement($sql)->execute();
	}

}
Unbekannter Benutzer ·

Hi,

danke für deinen Vorschlag, aber Statements sind keine gute Lösung. Ich möchte nicht auf SQL Basis arbeiten müssen. Außerdem wird das, wie du schon sagst, nicht voll unterstützt. Statements sind eher eine Notlösung.

Gibts da nichts anderes?

freshman17 ·

evtl. könntest du es mit der Pfadschreibweise lösen

in Tx_MyExt_Domain_Repository_CategoryRepository:

$query->equals('media.category', 'bar');

Ansonsten nur die Listen-Lösung die du jetzt schon hast oder die Statements, da gibt es sonst nichts!

FazzyX ·

Ich hatte ein ähnliches Problem, welches ich wie folgt lösen konnten.

Zunächst muss die Deklaration vom Attribute Kategorie stimmen.

/**
  * category
  * @var Tx_Extbase_Persistence_ObjectStorage<Tx_MyExt_Domain_Model_Category>
  */
protected $category;

Der findByCategories Methode im Repository wird eine Liste mit Kategorie Objekten übergeben.
Daraus wird das Query erstellt.

/**
  * 
  * @param array $categories of Tx_MyExt_Domain_Model_Category objects
  * @return Tx_MyExt_Domain_Model_Car 
  */
public function findByCategories($categories) {

        $query = $this->createQuery();

        foreach ($categories as $category) {
                $constraint[] = $query->contains('category', $category);
        }

        $result = $query->matching($query->logicalAnd($query->logicalAnd($constraint)))->execute();

        if (count($result) > 0) {
                return $result;
        }
}

Daraus ergibt sich dann ein Kombinationsfilter.

Grüße Claus

qualle85 ·
Typo3Cuckoo schrieb

Um nur kurz auf meinen ersten Beitrag in diesem Thema zurückzukommen, der Fehler lag (aus heutiger Sicht) bei der Query Abfrage mit equals. Ich hätte da schon mit contains arbeiten sollen, dann wäre das Problem gelöst und ich müsste nicht den Umweg über for-Schleifen machen.

Sollte also so funktionieren:

<?php
/**
 * 
 * Returns all objects of this repository with matching category
 * 
 * @param Tx_MyExt_Domain_Model_Category $category
 * 
 * @return Tx_Extbase_Persistence_QueryResult
 */
public function findByCategory(Tx_MyExt_Domain_Model_Category $category) {
	$query = $this->createQuery();
	$query->matching($query->contains('categories', $category));
	$result = $query->execute();
	return $result;
}
?>

Hi,

zuerst mal danke, dass du deine Lösung geteilt hast. Stehe vor einem ähnlichem Problem (1:n) aber komme nicht weiter. Erhalte die Meldung "Unsupported relation for contains" bei folgender Funktion:

/**
    * Finds Labeltemplates of a specific Baseproduct
    * 
    * @param Tx_Extbase_Persistence_ObjectStorage<xxx_Domain_Model_Baseproduct> $baseproduct
    * 
    * @return xxxLabelgenerator_Domain_Model_Labeltemplate
    */
    public function findByCategory(xxx_Domain_Model_Baseproduct $baseproduct) {
        $query = $this->createQuery();
        return $query->matching($query->contains('baseproducts', $baseproduct))->execute();
    }

Habe nun als Workaround eine Query geschrieben

public function findByCategoryRaw($baseproduct) {
        $query = $this->createQuery();
        return $query->statement('SELECT * FROM xxx_domain_model_labeltemplate WHERE FIND_IN_SET('.intval($baseproduct).', baseproducts);')->execute();
    }

aber das ist ja auch nicht Sinn der Sache.

Was habe ich denn vergessen zu konfigurieren? Hier mein TCA an der Stelle von xxx_domain_model_labeltemplate (child):

'baseproducts' => array(
			'exclude' => 1,
			'label' => 'LLL:EXT:...',
			'config' => array(
                            'type' => 'group',
                            'internal_type' => 'db',
                            'allowed' => 'xxx_domain_model_baseproduct', // parent
                            'minitems' => '0',
                            'maxitems' => '50',
                        ),
		),

Und im Modell von Baseproduct (parent):

public function getLabeltemplates() {
            if (!self::$labeltemplatesRepository) {
                self::$labeltemplatesRepository = t3lib_div::makeInstance('xxxLabelgenerator_Domain_Repository_Labeltemplaterepository');
            }
            #return self::$labeltemplatesRepository->findAll(); // works
            return self::$labeltemplatesRepository->findByCategory($this); // doesn't work
        }

Danke für Tips!

mchenzi ·

hallo
weis jemand ob das immernoch so ist oder obe das der extension BUilder heutzutage die Klassen dafür erstellt?
dank.