'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 1:[1]

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;
    }

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dan