Instantiate an Image with a static resource

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 !

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:

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 !

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.

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

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>

@bwaidelich How would the same code look like with fusion afx?

3 years later, wow…! :slight_smile:

if/else blocks are probably not the best use case for afx (and mostly there are better ways around) but you could do it with two .@if.hasAsset={author.avatar}
or a Neos.Fusion:Case for example.

@bwaidelich Thank you, that helped me!

I’m just searching for best practices with AFX and there is really not much documentation or code examples I could find.

In my case I’m looking for a fallback image if no image is given. How would you do that?

We hijacked this thread a little and I would suggest you open a new one if you want to discuss this further. But a very simple solution could look like this: FusionPen: Build, Test and Discover Fusion Code for Neos CMS