Skip to content

Single Directory Trait

The SingleDirectoryObjectListTrait trait can be used with ObjectListTrait to replicate behaviour similar to SDC, where component PHP code and assets (CSS and JavaScript) files are colocated, and then organised by component specific directories.

php
namespace Drupal\my_module\Pinto;

use Pinto\List\ObjectListTrait;
use Drupal\pinto\List\SingleDirectoryObjectListTrait; 

enum MyComponents: string implements ObjectListInterface {

  use ObjectListTrait;
  use SingleDirectoryObjectListTrait; 
  
  public function directory(): string { 
    return '/a/directory/with/shared/resources'; 
  } 
}

Example

Here, a design system module for a site has a list with references to all components.

  • The #[Definition] attribute references components in a different module.

  • The single directory trait resolves the PHP class, template, and assets (JS and CSS) to the same directory where the PHP component is located, using PHP reflection.

  • The templateName() method is overridden to customize the template file name. Since only one HTML file in a directory is used, the file name is normalized to template.html.twig. Otherwise, the enum name would be used, in this case: mycomponent.html.twig.

modules
├─ my_ds
│  └─ src
│    └─ Pinto
│      └─ MyComponents.php
└─ my_components
   └─ src
     └─ Components
       └─ MyComponent
          ├─ MyComponent.php
          ├─ template.html.twig
          ├─ styles.css
          └─ script.js
php
// my_ds/src/Pinto/MyComponents.php
namespace Drupal\my_ds\Pinto;

use Drupal\my_components\Components\MyComponent\MyComponent;
use Drupal\pinto\List\SingleDirectoryObjectListTrait; 
use Pinto\Attribute\ObjectType\Slots;
use Pinto\List\ObjectListTrait;

#[Slots(bindPromotedProperties: true)]
enum MyComponents implements ObjectListInterface {

  use ObjectListTrait;
  use SingleDirectoryObjectListTrait; 
  
  #[Definition(MyComponent::class)]
  case MyComponent;
  
  public function templateName(): string {
    // Customise Twig file name to template.html.twig.
    return 'template'; 
  }

  public function directory(): string {
    return '/the/directory/containing/the/component/class/';
  }
}
php
// my_components/src/Components/MyComponent/MyComponent.php
namespace Drupal\my_components\Components\MyComponent;

use \Pinto\Attribute\Asset\Css;
use \Pinto\Attribute\Asset\Js;

#[Js('script.js')] 
#[Css('styles.css')] 
final readonly class MyComponent {
  public function __construct(
    public readonly string $title,
  ) {}
}
twig
<!-- my_components/src/Components/MyComponent/template.html.twig -->
<div class="my-component">
  {{ title }}
</div>
javascript
/* my_components/src/Components/MyComponent/script.js */
console.log('Hello World!');
css
/* my_components/src/Components/MyComponent/styles.css */
.my-component {
  border: 2px solid orange;
}