PHPackages                             demos-europe/edt-paths - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. demos-europe/edt-paths

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

demos-europe/edt-paths
======================

Tools to create paths between entities and entity properties.

0.26.0(1y ago)222.1k↓23.2%2MITPHPPHP ^8.1

Since Jun 23Pushed 1y ago1 watchersCompare

[ Source](https://github.com/demos-europe/edt-paths)[ Packagist](https://packagist.org/packages/demos-europe/edt-paths)[ RSS](/packages/demos-europe-edt-paths/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (4)Versions (112)Used By (2)

Tools to create paths between entities and entity properties.

Provides PHP classes to enable you to use phpdoc tags to annotate classes as path segment and reference other path segments to define a directed graph, which can be used to build property paths for conditions and sort methods with IDE support.

Overview
--------

[](#overview)

This component enables you to mark classes as path segment and reference such classes in other classes as relationships.

To give an example suppose we have a class `Comment` with a relationship to the class `Article`which in turn has a relationship to the class `Author`. If we want to build a path using the property names of the relationships we could define an array like the following manually: `['article', 'author']`. For this you not only have to know the property names at the time you write the path but also need to adjust all paths using a property when the name of the property changes.

An alternative would be to define string constants in each class with the names of each property. In this case you could do something like this `[Comment::ARTICLE, Article::AUTHOR]`. This way you can change the value of the constant without adjusting each path manually. Also, your IDE can help you find each usage of a property in your application.

This component extends on these advantages and uses phpdoc `@property-read` tags instead of constants. As a result you can define the path with the following expression: `$comment→article→author→getAsNames()`. The result will be the same as in the examples above. This way your IDE provides you with a list of available relationships at each segment and can keep track of the usage of each `@property-read`, allowing for easy property renaming.

To enable a class as path segment and add working `@property-read` tags you only need to add the `PropertyAutoPathTrait` as `use` inside your class. After that you can add `@property-read` tags with the type of the target class as shown below for the `Article` class.

```
/**
 * @property-read Author $author
 * @property-read Comment $comments
 */
class Article
{
    use \EDT\PathBuilding\PropertyAutoPathTrait;

    private $text;

    public function __construct(string $text)
    {
        $this->text = $text;
    }

    public function getText(): string
    {
        return $this->text;
    }
}
```

By default, the constructor of your class will be ignored and a not fully initialized instance will be returned. This means that you may not be able to use the instances resulting from a path for anything else but further path building. So the above class will work for `$comment→article→authors→getAsNames()` but will fail for `$comment→article→getText()`.

However, if you pass a factory callback to the trait the path segments will be created using that callback, resulting in fully usable instances of your class. Such factory will depend on your system architecture. The following example can only give a rough idea, your solution may differ.

```
/**
 * @property-read Author $author
 * @property-read Comment $comments
 */
class Article
{
    use \EDT\PathBuilding\PropertyAutoPathTrait;

    private $text;

    public function __construct(string $text, ArticleRepository $articleRepository)
    {
        $this->childCreateCallback =
        static function (string $class, $parent, string $parentPropertyName) use ($articleRepository) {
            if ($parent instanceof Comment) {
                $articleId = $parent->getArticleId();
                $article = $articleRepository->getArticle($articleId);
                $article->setParent($parent);
                $article->setParentPropertyName($parentPropertyName);
                return $article;
            }
            if ($parent instanceof Author) {
                return $this->createChild($class, $parent, $parentPropertyName);
            }
            // ... more class checks
        };
        $this->text = $text;
    }

    public function getText(): string
    {
        return $this->text;
    }
}
```

Note that this example will fall back to the not fully initialized instances in case of to-many relationships. This is the case because it would otherwise need to select an `Article` instance in case of paths like `$author→articles`. Such paths can still be used for further path building like `$author→articles→comments→getAsNames()`. But like mentioned before it may fail if methods of the actual class are used, like in the following access: `$author→articles→getText()`. This is a known limitation considered acceptable, as the main usage is the building of paths, not a replacement of getters to fetch actual instances.

Supported tags
--------------

[](#supported-tags)

The example above uses the `property-read` tag to add properties to your path class. This is the default as it seems to make the most sense for the path building use case, however you can change it to other tags if desired as shown below.

```
/**
 * @property Author $author
 * @property Comment $comments
 */
class Article
{
    use \EDT\PathBuilding\PropertyAutoPathTrait;

    protected function getDocblockTraitEvaluator(): DocblockPropertyByTraitEvaluator
    {
        if (null === $this->docblockTraitEvaluator) {
            $this->docblockTraitEvaluator = PropertyEvaluatorPool::getInstance()->getEvaluator(PropertyAutoPathTrait::class, ['property']); // replaces the default 'property-read'
        }

        return $this->docblockTraitEvaluator;
    }
}
```

You can set multiple tags too, as is shown below for the `property-read` and `property` tags.

```
/**
 * @property Author $author
 * @property Comment $comments
 */
class Article
{
    use \EDT\PathBuilding\PropertyAutoPathTrait;

    protected function getDocblockTraitEvaluator(): DocblockPropertyByTraitEvaluator
    {
        if (null === $this->docblockTraitEvaluator) {
            $this->docblockTraitEvaluator = PropertyEvaluatorPool::getInstance()->getEvaluator(PropertyAutoPathTrait::class, ['property-read', 'property']);
        }

        return $this->docblockTraitEvaluator;
    }
}
```

Supported tags are:

- `property`
- `property-read`
- `property-write`
- `param`
- `var`

Credits and acknowledgements
----------------------------

[](#credits-and-acknowledgements)

Conception and implementation by Christian Dressler with many thanks to [eFrane](https://github.com/eFrane).

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance41

Moderate activity, may be stable

Popularity30

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity63

Established project with proven stability

 Bus Factor1

Top contributor holds 99.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Recently: every ~117 days

Total

111

Last Release

385d ago

PHP version history (5 changes)0.6.1PHP &gt;=7.3,&lt;8.2

0.8.0PHP &gt;=7.3,&lt;8

0.13.0PHP &gt;=7.4,&lt;8

0.14.1PHP ^7.4 || ^8.0

0.17.5PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/b2998f0a3cfbccb2c469bde14aa00b8f61fb12380a7785213927cbaf033e4f12?d=identicon)[demosci](/maintainers/demosci)

---

Top Contributors

[![actions-user](https://avatars.githubusercontent.com/u/65916846?v=4)](https://github.com/actions-user "actions-user (266 commits)")[![graupnerdemos](https://avatars.githubusercontent.com/u/40465804?v=4)](https://github.com/graupnerdemos "graupnerdemos (1 commits)")

### Embed Badge

![Health badge](/badges/demos-europe-edt-paths/health.svg)

```
[![Health](https://phpackages.com/badges/demos-europe-edt-paths/health.svg)](https://phpackages.com/packages/demos-europe-edt-paths)
```

###  Alternatives

[symfony/maker-bundle

Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.

3.4k111.1M568](/packages/symfony-maker-bundle)[roave/backward-compatibility-check

Tool to compare two revisions of a public API to check for BC breaks

5953.3M56](/packages/roave-backward-compatibility-check)[coenjacobs/mozart

Composes all dependencies as a package inside a WordPress plugin

4723.6M20](/packages/coenjacobs-mozart)[psalm/plugin-laravel

Psalm plugin for Laravel

3274.9M308](/packages/psalm-plugin-laravel)[phpdocumentor/reflection

Reflection library to do Static Analysis for PHP Projects

12521.4M109](/packages/phpdocumentor-reflection)[cognesy/instructor-php

The complete AI toolkit for PHP: unified LLM API, structured outputs, agents, and coding agent control

310107.9k1](/packages/cognesy-instructor-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
