# Laminas Atom Feed with custom extension

**URL:** <https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416>\
**Category:** Components & MVC\
**Tags:** laminas-feed\
**Created:** [March 7, 2020, 6:57am UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416 "2020-03-07T06:57:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![fansanelli](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/fansanelli/32/625_2.png) [@fansanelli](https://discourse.laminas.dev/u/fansanelli)\
**Post date:** [March 7, 2020, 6:57am UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/1 "2020-03-07T06:57:16Z")

</div>

Hello,

I’m trying to build my own Extension like the ones provided in tree.  
My goal is to show inside Atom xml events so this is an example I will add more tags:

- start datetime
- finish datetime
- availablity
- thumbnail and big picture
- address information

With this code and I’m able to register it:

```auto
$extensionManager = \Laminas\Feed\Writer\Writer::getExtensionManager();
$extensionManager->add('MyExtension\Renderer\Entry', \MyExtension\Renderer\Entry::class);
Laminas\Feed\Writer\Writer::registerExtension('MyExtension');

```

This is my class:

```auto
<?php

namespace MyExtension\Renderer;

use DOMDocument;
use DOMElement;
use Laminas\Feed\Writer\Extension;

/**
*/
class Entry extends Extension\AbstractRenderer
{
    /**
     * Set to TRUE if a rendering method actually renders something. This
     * is used to prevent premature appending of a XML namespace declaration
     * until an element which requires it is actually appended.
     *
     * @var bool
     */
    protected $called = false;

    /**
     * Render entry
     *
     * @return void
     */
    public function render()
    {
        if (strtolower($this->getType()) === 'rss') {
            return; // Atom 1.0 only
        }
        $this->_setImageLink($this->dom, $this->base);
        if ($this->called) {
            $this->_appendNamespaces();
        }
    }

    /**
     * Append namespaces to entry root
     *
     * @return void
     */
    // @codingStandardsIgnoreStart
    protected function _appendNamespaces()
    {
        // @codingStandardsIgnoreEnd
        $this->getRootElement()->setAttribute(
            'xmlns:solg',
            'http://example.it/atom/1.0'
        );
    }

    /**
     * Set image link
     *
     * @param DOMDocument $dom
     * @param DOMElement $root
     * @return void
     */
    // @codingStandardsIgnoreStart
    protected function _setImageLink(DOMDocument $dom, DOMElement $root)
    {
        // @codingStandardsIgnoreEnd
        $link = $this->getDataContainer()->getImageLink();
        if (! $link) {
            return;
        }
        $clink = $this->dom->createElement('image_link');
// $clink->setAttribute('rel', 'replies');
// $clink->setAttribute('type', 'text/html');
        $clink->setAttribute('href', $link);
// $count = $this->getDataContainer()->getCommentCount();
// if ($count !== null) {
// $clink->setAttribute('thr:count', $count);
// }
        $root->appendChild($clink);
        $this->called = true;
    }

}
?>

```

Unluckily it breaks on render:

```auto
[Fri Mar 06 17:14:45.214753 2020] [php7:notice] [pid 28374] [client 127.0.0.1:36146] PHP Fatal error: Uncaught Laminas\\Feed\\Writer\\Exception\\BadMethodCallException: Method: getImageLink does not exist and could not be located on a registered Extension in /home/francesco/eclipse-workspace/project/vendor/laminas/laminas-feed/src/Writer/Entry.php:724
Stack trace:
#0 /home/francesco/eclipse-workspace/project/MyExtension/Renderer/Entry.php(70): Laminas\\Feed\\Writer\\Entry->__call('getImageLink', Array)
#1 /home/francesco/eclipse-workspace/project/MyExtension/Renderer/Entry.php(38): MyExtension\\Renderer\\Entry->_setImageLink(Object(DOMDocument), Object(DOMElement))
#2 /home/francesco/eclipse-workspace/project/vendor/laminas/laminas-feed/src/Writer/Renderer/Entry/Atom.php(59): MyExtension\\Renderer\\Entry->render()
#3 /home/francesco/eclipse-workspace/project/vendor/laminas/laminas-feed/src/Writer/Renderer/Feed/Atom.php(90): Laminas\\Feed\\Writer\\Renderer\\Entry\\Atom->render()
#4 /home/francesco/eclipse-workspace/project/vendor/laminas/laminas-feed/src/Writer/Feed.php(237): Laminas\\Feed\\Writer\\Renderer\\Feed\\Atom-> in /home/francesco/eclipse-workspace/project/vendor/laminas/laminas-feed/src/Writer/Entry.php on line 724

```

I’m not sure how to set the new property and how make it run without breaking on:

```auto
$feed->export('atom');

```

Any suggestion is welcome.  
Many thanks  
Cheers  
Francesco

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [March 7, 2020, 9:07pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/2 "2020-03-07T21:07:04Z")

</div>

Hello and welcome to the Laminas forums!

One part is missing in your extension; the feed entry which holds the data of your image link. At the moment you have only created the renderer for your feed.

Some **untested code** to illustrate the entry class which holds the link:

```php
namespace MyExtension;

use BadMethodCallException;
use Laminas\Stdlib\StringUtils;
use Laminas\Stdlib\StringWrapper\StringWrapperInterface;

class Entry
{
    /** @var array */
    protected $data = [];

    /** @var string */
    protected $encoding = 'UTF-8';

    /** @var StringWrapperInterface */
    protected $stringWrapper;

    public function __construct()
    {
        $this->stringWrapper = StringUtils::getWrapper($this->encoding);
    }

    public function setEncoding($enc)
    {
        $this->stringWrapper = StringUtils::getWrapper($enc);
        $this->encoding = $enc;

        return $this;
    }

    public function getEncoding()
    {
        return $this->encoding;
    }

    public function setImageLink($value)
    {
        $this->data['imageLink'] = $value;
    }

    public function __call($method, array $params)
    {
        $point = lcfirst(substr($method, 3));
        if (! method_exists($this, 'setImageLink')) {
            throw new BadMethodCallException(
                'invalid method: ' . $method
            );
        }
        if (! array_key_exists($point, $this->data)
            || empty($this->data[$point])
        ) {
            return;
        }

        return $this->data[$point];
    }
}

```

Then add both to the extension manager and register the extension itself:

```php
$extensionManager = Laminas\Feed\Writer\Writer::getExtensionManager();

// Add entry with image link
$extensionManager->add(
    MyExtension\Entry::class,
    MyExtension\Entry::class
);

// Add renderer for entry with image link 
$extensionManager->add(
    MyExtension\Renderer\Entry::class,
    MyExtension\Renderer\Entry::class
);

// Register extension
Laminas\Feed\Writer\Writer::registerExtension('MyExtension');

```

Compare also with the “Google Play Podcast” extension:

- [`Laminas\Feed\Writer\Extension\GooglePlayPodcast\Entry`](https://github.com/laminas/laminas-feed/blob/master/src/Writer/Extension/GooglePlayPodcast/Entry.php)
- [`Laminas\Feed\Writer\Extension\GooglePlayPodcast\Renderer\Entry`](https://github.com/laminas/laminas-feed/blob/master/src/Writer/Extension/GooglePlayPodcast/Renderer/Entry.php)

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [March 8, 2020, 9:30am UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/3 "2020-03-08T09:30:27Z")

</div>

Btw. your new element looks like the image link in [“Google Merchant Center” for product feeds](https://support.google.com/merchants/answer/160593?hl=en).

---

<div class="post-metadata">

**Author:** ![fansanelli](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/fansanelli/32/625_2.png) [@fansanelli](https://discourse.laminas.dev/u/fansanelli)\
**Post date:** [March 8, 2020, 1:36pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/4 "2020-03-08T13:36:51Z")

</div>

Thanks for the suggestions I’ll test it tomorrow…  
As I stated in my post I’ll need more functions to show events data.

Cheers  
Francesco

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [March 8, 2020, 2:14pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/5 "2020-03-08T14:14:31Z")

</div>

> [@fansanelli](#):
>
> As I stated in my post I’ll need more functions to show events data.

Please keep in mind that no feed reader understand your extensions and you might get an invalid feed.

Have a look at the RSS 2.0 specification:

> RSS 2.0 adds that capability, following a simple rule. A RSS feed may contain elements and attributes not described on this page, only if those elements and attributes are defined in a namespace.

> **[RSS 2.0 Specification (Current)](https://www.rssboard.org/rss-specification)**
>
> The current version of the RSS 2.0 specification (2.0.11), published on March 30, 2009.

An example can be found via [W3C feed validator](https://validator.w3.org/feed/docs/rss2.html#extendingRss): [dive into mark](http://static.userland.com/gems/backend/rssMarkPilgrimExample.xml)

I hope this helps to find a correct solution.

---

<div class="post-metadata">

**Author:** ![fansanelli](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/fansanelli/32/625_2.png) [@fansanelli](https://discourse.laminas.dev/u/fansanelli)\
**Post date:** [March 8, 2020, 2:56pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/6 "2020-03-08T14:56:01Z")

</div>

Thanks for all the help.  
I was thinking to an atom only extension because of this… I’ll provide all mandatory tags and add some extra one to give the ability to callers to implement filters, sort and extra information

---

<div class="post-metadata">

**Author:** ![fansanelli](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/fansanelli/32/625_2.png) [@fansanelli](https://discourse.laminas.dev/u/fansanelli)\
**Post date:** [March 9, 2020, 1:38pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/7 "2020-03-09T13:38:39Z")

</div>

I made it… I think you can start putting mine and your code together to update the documentation and close the thread.  
Thanks  
Francesco

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [March 9, 2020, 2:50pm UTC](https://discourse.laminas.dev/t/laminas-atom-feed-with-custom-extension/1416/8 "2020-03-09T14:50:54Z")

</div>

> [@fansanelli](#):
>
> …update the documentation…

This is already on my list.

> [@fansanelli](#):
>
> …close the thread.

You can mark the post that presents the solution for you. (See the buttons under every post.)
