news icon indicating copy to clipboard operation
news copied to clipboard

[FEATURE] Allow developers to define their own view

Open AKaravas opened this issue 2 years ago • 3 comments

Now days a lot of websites are taking the headless approach, so i figured that the news extension should give the ability to the developers to use the JsonView if needed or any other view for that matter.

I added an event and a getView function which gets the view and appends the developer's wished view to the called action. It can be done for every action or individual actions. An example would be:

Create the listeners

Vendor\Ext\EventListener\NewsInitializeActionListener:
    tags:
      - name: event.listener
        identifier: 'newsInitializeExtend'
        method: 'NewsInitializeActionEvent'
        event: GeorgRinger\News\Event\NewsInitializeActionEvent

If every action should adopt the developer's wished view:

use TYPO3\CMS\Extbase\Mvc\View\JsonView;

final class NewsInitializeActionListener
{
    public function NewsInitializeActionEvent(NewsInitializeActionEvent $event): void
    {
        $event->setDefaultViewObjectName(JsonView::class);
    }
}

If only a specific action should adopt the developer's wished view:

use TYPO3\CMS\Extbase\Mvc\View\JsonView;

final class NewsInitializeActionListener
{
    public function NewsInitializeActionEvent(NewsInitializeActionEvent $event): void
    {
        if ($event->getAction() === 'list') {
            $event->setDefaultViewObjectName(JsonView::class);
        }
    }
}

Now if we want to use the JsonView, the $this->view->assignMultiple($event->getAssignedValues()); is not enough. We have to tell the JsonView which variables should be rendered. This can be done like this. I am using the listAction as an example:

Vendor\Ext\EventListener\NewsListActionListener:
    tags:
      - name: event.listener
        identifier: 'newsListExtend'
        method: 'NewsListActionEvent'
        event: GeorgRinger\News\Event\NewsListActionEvent
final class NewsListActionListener
{
    public function NewsListActionEvent(NewsListActionEvent $event): void
    {
        $event->getNewsController()->getView()->setVariablesToRender(array_keys($assignedValues));
    }
}

This will render everything as json and no templates are used. The only problem you will come across, is that the news entries are objects and can not be converted to json properly. In this case you have two options (AFAIK):

  1. Iterate through the news entries, create a new array with the entries converted to array and append the results to $event->getAssignedValues()['news']. A small example would be the following.
final class NewsListActionListener
{
    public function NewsListActionEvent(NewsListActionEvent $event): void
    {
        $news = $event->getAssignedValues()['news'];
        $assignedValues = $event->getAssignedValues();
        unset($assignedValues['news']);
        if (count($news) > 0) {
            $newArray = [];
            $i = 0;
            foreach ($news as $singleNew) {
                /** @var News $singleNew */
                $newArray[$i]['uid'] = $singleNew->getUid();
                $newArray[$i]['title'] = $singleNew->getTitle();
                $newArray[$i]['bodytext'] = $singleNew->getBodytext();
                $newArray[$i]['pathSegment'] = $singleNew->getPathSegment();
                $i++;
            }
            $assignedValues['news'] = $newArray;
            $event->setAssignedValues($assignedValues);
        }

        $event->getNewsController()->getView()->setVariablesToRender(array_keys($assignedValues));
    }
}

This works perfectly, the only con is that you have to manually iterate and select the values to append to the new created array. The same applies for the files (images, pdfs etc). You have to manually generate the links to the target file.

  1. The other way would be the ->setConfiguration() part of the JsonView.
final class NewsListActionListener
{
    public function NewsListActionEvent(NewsListActionEvent $event): void
    {
         $event->getNewsController()->getView()->setConfiguration([
            'news' => [
                '_descendAll' => [
                    '_recursive' => ['categories', 'falMedia', 'contentElements', 'tags']
                ],
            ]
        ]);
        $event->getNewsController()->getView()->setVariablesToRender(array_keys($event->getAssignedValues()));
     }
}

Pros:

  1. Much less code
  2. TYPO3 API is used.
  3. Relations are resolved

Cons:

  1. Relations are resolved but the array indexes are not 0, 1, 2 etc, but, the objectStorage index e.g. 000000006ec89aaa00000000382b1662.
  2. File Relations are resolved but the file object is not retrieved, only it's UID found on the sys_file_reference. I havent found any reference to the JsonView that allows to retrieve the file object.

Last but not least, you can take the headless approach, which in this case, you do not need any of the above. The only disadvantage is that you ll have to create the json output in your .html Templates. The headless extension has actually created an extended version of the news extension to make it headless. https://github.com/TYPO3-Headless/headless_news. But if you got to the Resources and look at the files, everything is converted to json in the .html files. This is very sensitive, because a space or a line not on the right position can cause the json output to be converted to string and can be very tedious.

If anyone can make it better, please do not hesitate to edit the code!

AKaravas avatar May 25 '22 09:05 AKaravas

I regret to disappoint you but the option of JSON view you've already with https://github.com/TYPO3-Headless/headless_news

DavidBruchmann avatar May 25 '22 09:05 DavidBruchmann

I know, i have written my thought for it at the bottom of my comment

AKaravas avatar May 25 '22 09:05 AKaravas

Plus you talk about the JsonView. I tried to make it so that every view can be used. Self made or a core one. The JsonView was just an example because it the most used one after the TemplateView

AKaravas avatar May 25 '22 09:05 AKaravas