Instantiate an Image with a static resource


(George Dimitriadis) #1

Hello,

I have a model with an image property

/**
 * @var Neos\Media\Domain\Model\Image
 * @ORM\OneToOne
 */
protected $avatar;

And the respective method to get the avatar

/**
 * Get Avatar
 *
 * @return Neos\Media\Domain\Model\Image
 */
public function getAvatar()
{
    return $this->avatar;
}

In Fluid I am rendering the avatar with the media view helper

<media:thumbnail asset="{author.avatar}" />

Pretty straight forward so far. Now, the problem is the case where my model has no avatar set, where I would like to have a default static image from my public directory. However, the Image class needs a persistent resource in its constructor. After a bit of looking around I ended up using importImage to get a persistent resource, but I would like to know about potential pitfalls.

Here is my approach

/**
 * Get Avatar
 *
 * @return Image
 */
public function getAvatar()
{
    $avatar = $this->avatar;

    if (empty($avatar)) {
        $pResource = $this->resourceManager->importResource('https://imagejournal.org/wp-content/uploads/bb-plugin/cache/23466317216_b99485ba14_o-panorama.jpg');
        $avatar = new Image($pResource);
    }

    return $avatar;
}

While this works nicely, I would like to know a few things:

  1. Will this importResource call create a new image every time it’s called on a different model ?
  2. Is the image saved somewhere permanently ? I would like not to have this static image “polute” my database more than once.
  3. Performance-wise this seems to be slow in a page with many authors who have no avatar set, can I do something to improve this ?
  4. Are there better approaches for the Back-end ? I know I could handle this in the front-end with a check and passing a URI instead of an image, but I want to have a generic back-end solution.

Thanks a lot in advance !


(Soren Malling) #2

Yes

It’s (per default) saved to the Data/Persistent folder and added to the resource object table of your database

Don’t do this (solution coming up) - keep reading

Since frontend and backend are more or less the same, what you could do is this

Background first

The Neos.Media is highly depending on a Persistent Resource - this is due to it’s ability of using the resource management API of Flow, so you can seamlessly use Amazon S3, Dropbox, local storage or whatever for your files.

My suggestion

  1. Create your own ViewHelper for rendering. It’s not written in stone, that you have to use the ViewHelper from Neos.Media. Adopt and adapt the functionality that is usable. Pass your Model as a argument to the viewhelper and do somethig like this
public functino render(MyModel $object)  {
  $resourceManager = new ResourceManager();
  if ($object->hasAvatar()) {
    return $resourceManager->getPublicPersistentResourceUri($this->getAvatar()->getThumbnail(...)->getResource());
  } else {
    return $resourceManager->getPublicPackageResourceUri('Your.PackageName', 'Public/Images/DefaultAvatar.png');
  }
}

This will return a uri string to either a default avatar or a avatar of the object.

And you can even implement this directly into your model (I don’t see a issue concerning business logic - some might disagree - please enlighten me) and it’s completely transparent in terms of usage in other context such as CLI

Have fun! :slight_smile:


(George Dimitriadis) #3

Hi Soren,

thanks for your suggestion.

I checked the resource table and indeed the resource is there. I guess I’ll make a query to fetch the resource by its unique filename or some other method from the database and import it if it’s not there.

Your solution is nice, but it still forces me to lose the advantages of the media view helper, such as the given size of the image that I define in the front-end, so I’ll try the above idea.

Cheers !


(Soren Malling) #4

What you can do, is to extend the orignal ViewHelper and then call the render method from the extended ViewHelper if you have a Avatar, or else go with the static resource.


(George Dimitriadis) #5

That sounds interesting, I’ll give it a try, thanks Soren !


(Bastian Waidelich) #6

I would suggest not to use an entity (= asset) for the fallback image, but implement a Author::hasAvatar() getter and do sth like this:

<f:if condition="{author.avatar}">
    <f:then>
        <media:thumbnail asset="{author.avatar}" />
    </f:then>
    <f:else>
        <img src="{f:uri.resource(package: 'Your.Package', path: 'Images/dummy-image.svg')}" />
    </f:else>
</f:if>