Get internal server path to files in Shopware twig

How to access file system paths from storefront twig files in Shopware.

99 views
d

By. Jacob

Edited: 2024-03-12 13:54

Internal server-side file paths are not available in .twig files, and instead you will be working with namespaced paths; to a PHP developer that is used to work with more universally understood file system paths, this is very confusing. But, you can work around this limitation by creating a custom twig function that exposes the paths you need in your storefront .twig files.

This is done by creating a nameOfPlugin/src/Twig/AppExtension.php file in a plugin or theme – your own twig extensions should go to this file.

Here is an example file that you might use. If the file does not exist already, you will need to first create it:

<?php

namespace myPluginNamespace\Twig;

use Exception;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Shopware\Storefront\Theme\MD5ThemePathBuilder;
use SimpleXMLElement;
use Symfony\Component\HttpFoundation\RequestStack;

class AppExtension extends AbstractExtension
{
  private string $basePath, $storefrontBasePath;
  private $containerBag, $requestStack;

  public function __construct(ContainerBagInterface $containerBagInterface, RequestStack $requestStack)
  {
    $this->containerBag = $containerBagInterface;
    $this->requestStack = $requestStack;

    $kernel_bundles_metadata = $this->containerBag->get('kernel.bundles_metadata');

    // Obtain the project "base path" from kernal.project_dir
    $this->basePath = $this->containerBag->get('kernel.project_dir') . '/';
    $this->storefrontBasePath = $kernel_bundles_metadata['Storefront']['path'] . '/';
  }

  public function getFunctions()
  {
    return [
      new TwigFunction('storefrontBasePath', [$this, 'storefrontBasePath'])['html']])
    ];
  }

  /**
   * Returns the server root path for the @Storefront
   * @return string 
   */
  public function storefrontBasePath()
  {
    return $this->storefrontBasePath;
  }
}

Then, if you need the server-side path for the Storefront, you can now call storefrontBasePath from your twig files:

<p>Path to storefront: {{ storefrontBasePath() }}</p>

Shopware uses Namespaced Twig Paths

I do not know if this is universal to apps that relies on twig, but one thing you will notice in Shopware, at least, is the use of the at (@) syntax to refer to specific key locations in the file system. I personally dislike this syntax greatly, because it is an unnecessary abstraction that hides important information to the developer and increases complexity.

Mind you, the syntax also works for plugins. So, if you have used a standardized file hierarchy in a home made plugin, you will be able to access the storefront of your plugin with @NameOfPlugin/storefront/layout/some-file.html.twig – this however raises a major complaint about this abstraction, and that is the fact that it is super confusing to know what exactly you are supposed to write in order to access specific folders. In this instance, the behaviour of @NameOfPlugin/storefront becomes "magical" in that it actually "abstracts away" parts of the physical path. E.g. The real physical path becomes:

custom/plugins/myPlugInName/src/Resources/views/storefront/

This adds an inhumane amount of abstraction, obfuscation, and ultimately cognitive friction for developers trying to learn and figure out why something they want to include/link is not showing up.

To make matters worse, you will not intuitively be be able to use same syntax within PHP. That is, you cannot simply call PHP's standard file_get_content function on namespaced paths. Interestingly, the syntax is also used in services.xml, which has caused me to waste a lot of time on debugging "class not found" issues when declaring my services. Why do we intentionally convolute things so badly?

Needless to say, this friction could have been avoided simply by sticking with what is understood almost universally: actual file paths.

I have of course learned to work with these "namespaced paths", but my personal preference is without doubt leaning towards not convoluting things unnecessarily for newcomers. If you must, why not at least provide decent debug information? E.g. If a file or class is not found, then show precisely which physical paths was probed by the system when looking for it, even something that simple could be a huge help when dealing with both paths and namespaced paths.

Links

  1. Namespaced Paths - symfony.com

Tell us what you think:

  1. Sometimes we may to manually clear cached Shopware files to fix namespace issues.
  2. How to obtain the currently selected API language from Shopware vue components.
  3. How to get or change the currently selected sales channel in an Shopware sw-sales-channel-switch component.
  4. In this tutorial you will learn how to work with Shopware entities in a generic way from PHP, without having to specifically inject the repository in your services.xml file.

More in: Shopware