18.10.2011

Typo3 Extbase Workspaces Preview

Problem: In Extbase werden Datensätze im Preview manchmal nicht richtig angezeigt. Das betrifft Typo3-Datensätze, die in Relation (n-m) zum eigentlichen Haupt-Datensatz stehen und zusammen mit diesem im Frontend dargestellt werden sollen. Der Hauptdatensatz wird als Preview-Version angezeigt, die relationalen Datensätze werden aber aus dem Live-Workspace genommen.

Als Beispiel: Wenn ein (in Extbase selbst programmierter) Newsartikel zum seinem Thema verwandte Artikel als Teaser anzeigen soll, tritt dieses Problem auf. Der Newsartikel kommt aus dem Preview-Workspace, die Teaser der verwandten Artikel kommen aus dem Live-Workspace.

Wenn man im Preview jetzt aber alles so anzeigen möchte wie im Preview zugewiesen, hilft vielleicht dieser Workaround:

Die relationalen Datensätze werden über ihr eigenes Repository an das übergeordnete Model ausgeliefert. Deshalb könnte man innerhalb des Repositorys eine Funktion definieren, die die Objekte aus dem jeweiligen Preview-Workspace zurückliefert anstelle der Live-Daten. Ich hab also eine Klasse Baserepository geschrieben, von der alle meine anderen Repositories erben. In den Querys muss dann noch respectEnableFields auf false gesetzt werden.

<?php

/**
 * Description of BaseRepository
 *
 * @author tim schenk http://www.tim-schenk.de
 */
class Tx_MyExtension_Domain_Repository_BaseRepository extends Tx_Extbase_Persistence_Repository
{
    /**
     * static table->uid array
     * @var array array('Table'=>array('PlaceholderUid'=>'WorkspaceUid'),'nextTable'=>array(...),...);
     */
    private static $idsInWorkspace = array();
    /**
     * table name for this repository
     * @var string
     */
    protected $table = "";
    /**
     * Gets the Workspace ID
     * @return string Workspace ID (default 0 = Live)
     */
    protected function getWsId()
    {
        //if both fails return zero
        $wsId = 0;
        //Backend
        if (!empty($GLOBALS['BE_USER']->workspace))
        {
            $wsId = $GLOBALS['BE_USER']->workspace;
        }
        //Frontend
        elseif (!empty($GLOBALS['TSFE']->sys_page->versioningWorkspaceId))
        {
            $wsId = $GLOBALS['TSFE']->sys_page->versioningWorkspaceId;
        }

        return $wsId;
    }

    /**
     * If its a preview the integer should be greater zero
     * @return integer Workspace ID (default 0 = Live)
     */
    public function isPreview()
    {
        return $this->getWsId();
    }

    /**
     * there might be a standard typo3 function for that
     * @param string $table name of table
     * @return string delete clause sql
     */
    protected function getDeletedClause($table)
    {
        return "$table.deleted=0";
    }

    /**
     * there might be a standard typo3 function for that
     * @param string $table name of table
     * @return string hidden clause sql
     */
    protected function getHiddenClause($table)
    {
        $where = $this->getDeletedClause($table);
        $where .= " AND $table.hidden=0";
        if (!$this->isPreview())
        {
            $where .= " AND ( $table.starttime=0 OR $table.starttime<=" . time() . " )";
            $where .= " AND ( $table.endtime=0 OR $table.endtime>=" . time() . " )";
        }
        return $where;
    }

    /**
     * createQuery overrides parent function
     * @return Tx_Extbase_Persistence_QueryInterface $q
     */
    public function createQuery()
    {
        $q = parent::createQuery();
        // show also hidden fields etc...
        $q->getQuerySettings()->getRespectEnableFields(false);
        return $q;
    }
/**
 * returns the sql string for the actual workspace
 * @param string $table
 * @return string workspace sql clause
 */
    public function getWsClause($table)
    {
        //get workspace id
        $wsId = $this->getWsId();
        $where = "";

        if ($wsId == 0)
        {//Live workspace
            $where .= " $table.pid<>-1 ";
            $where .= " AND $table.t3ver_wsid=0 ";
            if (TYPO3_MODE == 'FE')
            {
                //includes deleted and startime / endtime
                $where .= " AND " . $this->getHiddenClause($table);
            }
            else
            {
                // dont return any deleted stuff
                $where .= " AND $table.deleted=0";
            }
        }
        else
        {//preview workspace
            $stageWhere = $this->getPrewiewStageClause($table);
            if (!empty($stageWhere))
            {
                $where .= " $stageWhere";
            }
            else
            {
                $where .= " $table.pid<>-1";
            }
            // return both, workspace and preview items
            $where .= " AND $table.t3ver_wsid IN (0,$wsId)";
            $where .= " AND $table.deleted=0";

            if (TYPO3_MODE == 'FE')
            {
                $where .= " AND $table.hidden=0";
            }
        }
        return $where;
    }
    /**
     * gets an array of the uid in actual workspace
     * http://www.schmutt.de/303/eigene-extension-mit-workspace-versionierung/
     * http://www.ausgebloggt.de/2011/02/10/typo3-extbase-workspace-vorschau/
     * http://typo3.org/documentation/document-library/core-documentation/doc_core_api/4.1.0/view/3/2/
     *
     * @param string $table
     * @param bool $returnPlaceholderUids default = false
     * @return array of integers -> uids in workspace
     *     Array keys are the original uids
     *     Array Values are the workspace uids
     */
    protected function getWorkspaceConstraint($table, $returnPlaceholderUids = false)
    {
        $queryString = array();
        $wsId = $this->getWsId();
        // do this search only one time per process
        if (empty(self::$idsInWorkspace[$table]))
        {//static
            $ids = array();
            //deleted,hidden,sichtbare version, workspace
            $where = $this->getWsClause($table);
            $queryResult = $GLOBALS['TYPO3_DB']->exec_SELECTquery("*", $table, $where);
            while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($queryResult))
            {
                // deletes row if it is no valid version
                if (TYPO3_MODE == 'BE')
                {
                    t3lib_BEfunc::workspaceOL($table, $row);
                }
                else
                {
                    $GLOBALS['TSFE']->sys_page->versionOL($table, $row, true);
                }
                if (is_array($row))
                { //get uid of workspace version
                    $ids[$row['uid']] = $row['uid'];
                }
            }
            self::$idsInWorkspace[$table] = $ids;
        }
        $idArray = self::$idsInWorkspace[$table];
        if (!$returnPlaceholderUids)
        {
            foreach ($idArray as $key => $value)
            {
                $idArray[$key] = $this->getWsUid($table, $value);
            }
        }
        if (!count($idArray))
        {
            $idArray = array(0);
        }
        return $idArray;
    }
    /**
     * returns uid of actual workspace
     * @param string $table
     * @param integer $uid
     * @param string $mode BE or FE
     * @return integer $uid
     */
    public function getWsUid($table, $uid, $mode = 'FE')
    {
        if ($this->isPreview() && TYPO3_MODE == $mode)
        {
            return t3lib_BEfunc::wsMapId($table, $uid);
        }
        return $uid;
    }
}
?>


Im erbenden Objekt können dann nach bedarf die entsprechenden uids geholt werden. Zum Beispiel:

function getMyrelation(){
  $q = $this->createQuery();
  $wsConstraint = $this->getWorkspaceConstraint('tx_myextension_domain_model_mymodelclass');
  if(!empty($wsConstraint)){
     $q->matching($q->in('uid',$wsConstraint));
  }
  return $q->execute();
}


Das sollte so gehen...