phptypo3extbase

Show xml data in Frontend


Is it possible fetching data from a cached xml file and then showing them on front end?

I was thinking doing it in a TYPO3 extension and with its domain model (and getter/setter) but without a database table. And then filling in data with SimpleXML just to "store" them in memory. At least display the data from domain model with fluid on front end. But I don't know is this approach right or is there a better way to do that? In particular setting up the persistence layer I don't understand.

For any help I thank you very much for your effort in advance.


Solution

  • I found an "acceptable" solution. My approach for that was:

    1. Get all items from xml file
    2. Add a slug field
    3. Sort the items
    4. Display sorted items on the front end
    5. Create unique pretty url

    1. Get all items from xml file

    Controller: listAction, detailAction

    public function listAction() {
        $jobs = $this->xmlDataRepository->findAll();
        $jobsArray = $this->simpleXmlObjToArr($jobs);
        $jobsArraySorted = $this->sortJobsByTitle($jobsArray);
        $this->view->assign('jobs', $jobsArraySorted);
    }
    
    public function detailAction($slugid) {
        $job = $this->xmlDataRepository->findBySlugWithId($slugid);
        $this->view->assign('job', $job[0]);
    }
    

    Repository: findAll, findBySlugWithId

    public function findAll() {
        $objectStorage = new ObjectStorage();
        $dataFolder = ConfigurationService::setDataFolder();
        $xmlFile = glob($dataFolder . '*.xml')[0];
        $xmlData = simplexml_load_file($xmlFile,'SimpleXMLElement',LIBXML_NOWARNING);
    
        // error handling
        if ($xmlData === false) {
            ...
        }
    
        foreach($xmlData->children() as $job) {
            $objectStorage->attach($job);
        }
        return $objectStorage;
    }
    
    public function findBySlugWithId($slugid) {
        // get id from slugid
        $id = substr($slugid,strrpos($slugid,'-',-1)+1);
        $objectStorage = new ObjectStorage();
        $dataFolder = ConfigurationService::setDataFolder();
        $xmlFile = glob($dataFolder . '*.xml')[0];
        $xmlData = simplexml_load_file($xmlFile,'SimpleXMLElement',LIBXML_NOWARNING);
    
        // error handling
        if ($xmlData === false) {
            ...
        }
    
        $jobfound = false;
    
        foreach($xmlData->children() as $job) {
            if ($job->JobId == $id) {
                $objectStorage->attach($job);
                $jobfound = true;
            }
        }
    
        // throw 404-error
        if (!$jobfound) {
            $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
                $GLOBALS['TYPO3_REQUEST'],
                'Ihre angeforderte Seite wurde nicht gefunden',
                ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
            );
            throw new ImmediateResponseException($response, 9000006460);
        }
    
        return $objectStorage;
    }
    

    2. Add a slug field (controller)

    protected function simpleXmlObjToArr($obj) {
        // 2-dimensional array
        $array = [];
        foreach($obj as $item){
            $row = [];
            foreach($item as $key => $val){
                $row[(string)$key] = (string)$val;
            }
    
            //add slug field, build it with Title
            $row['Slug'] = $this->convertToPathSegment($row['Titel']);
    
            // add $row to $array
            array_push($array,$row);
        }
        return $array;
    }
    

    3. Sort the items (controller)

    protected function sortJobsByTitle(array $jobs) {
        $title = array();
        $id = array();
    
        foreach ($jobs as $key => $job) {
            $title[$key] = $job['Titel'];
            $id[$key] = $job['JobId'];
        }
    
        // sort jobs array according to title, uid (uid because if there are courses with the same title!)
        array_multisort($title,SORT_ASC, $id,SORT_ASC, $jobs,SORT_STRING);
    
        return $jobs;
    }
    

    4. Display sorted items on the front end (templates)

    List.html:

    ...
    <ul>
        <f:for each="{jobs}" as="job">
            <li>
                <f:comment>
                    <f:link.action class="" pageUid="2" action="show" arguments="{id: job.JobId, slug: job.Slug}">{job.Titel}</f:link.action> ({job.JobId})<br>
                    <f:link.action class="" pageUid="2" action="detail" arguments="{xml: job}">NEW {job.Titel}</f:link.action> ({job.JobId})
                </f:comment>
    
                <f:variable name="slugid" value="{job.Slug}-{job.JobId}"/>
                <f:link.action class="" pageUid="2" action="detail" arguments="{slugid: slugid}"><f:format.raw>{job.Titel}</f:format.raw></f:link.action> ({job.JobId})
            </li>
        </f:for>
    </ul>
    ...
    

    Detail.html:

    ...
    <f:image src="{job.Grafik}" width="500" alt="Detailstellenbild" />
    <p><strong><f:format.raw>{job.Titel}</f:format.raw></strong> ({job.JobId})</p>
    <p>Region: {job.Region}</p>
    <f:format.html>{job.Beschreibung}</f:format.html>
    ...
    

    5. Create unique pretty url

    ...
    routeEnhancers:
      XmlJobDetail:
        type: Extbase
        limitToPages:
          - 2
        extension: Wtdisplayxmldata
        plugin: Displayxmldata
        routes:
          -
            routePath: '/{job-slugid}'
            _controller: 'XmlData::detail'
            _arguments:
              job-slugid: slugid
        defaultController: 'XmlData::list'
        aspects:
          job-slugid:
            type: XmlDetailMapper
    

    Routing/Aspect/XmlDetailMapper.php:

    use TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface;
    use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
    
    class XmlDetailMapper implements StaticMappableAspectInterface {
    
        /**
         * {@inheritdoc}
         */
        public function generate(string $value): ?string
        {
             return $value !== false ? (string)$value : null;
        }
    
        /**
         * {@inheritdoc}
         */
        public function resolve(string $value): ?string
        {
             return isset($value) ? (string)$value : null;
        }
    
    }