0

A PHP trait cannot declare a constant, but is it possible to declare using a DocComment (or otherwise) a constant which the trait might "use" if the class using the trait defines it?

For example, imagine a class Book is a model bound to the books table. The framework requires that the table is defined as a TABLE constant:

class Books extends Model
{
    const TABLE = 'books';
}

When creating my model, my IDE autocompletes the TABLE constant because it's declared on Model. Wonderful.

Now, say I have a trait called Sluggable which is a trait which will help the developer manage the book's URL slug (e.g. treasure-island), and one of the configurations options is whether to automatically sluggify the book's title, or not. Say this is controlled by the AUTOMATIC_SLUGS constant.

trait Sluggable {
    public function generateSlug()
    {
        if (defined('static::AUTOMATIC_SLUGS') && static::AUTOMATIC_SLUGS) {
            // do the thing
        }
    }
}

Obviously, the trait cannot define the AUTOMATIC_SLUGS constant because that's not allowed, however the IDE cannot suggest, or otherwise verify the AUTOMATIC_SLUGS constant. So my model Books now has a warning on it (in my IDE) telling me that AUTOMATIC_SLUGS is unused:

class Books extends Model
{
    use Sluggable;

    const TABLE = 'books';

    // IDE complains about this constant
    const AUTOMATIC_SLUGS = true;
}

Is there a way for me to – on the trait, and without refactoring to use static properties – declare that Sluggable will be checking a constant called AUTOMATIC_SLUGS and have the IDE suggest it/not consider it useless?

HelloPablo
  • 615
  • 1
  • 7
  • 22
  • https://stackoverflow.com/a/35598531/12437108 perhaps something like this? – Tim Apr 28 '21 at 13:38
  • Although this doesn't help you now, there was [some discussion last year](https://externals.io/message/110741) about adding constants to traits and the overall consensus was that people wanted it and it shouldn't be too hard to implement. It might be worth checking in with them to see where it is at. – Chris Haas Apr 28 '21 at 13:46
  • For my own code, I think I would use an interface with methods for this. The trait method `generateSlug` could check if `$this` was an instance of that trait and if so, call the `shouldUseAutomaticSlugs()` method, and if not, assume a sane default. Classes using the trait then would be encouraged, but not required, to implement that interface. It is a lit extra work and probably not as performant as a constant, but it is also more explicit. I love traits, but when they become "self-aware" or reflect too much on the class I often start falling back to OOP stuff. – Chris Haas Apr 28 '21 at 13:56
  • Thanks everyone. Those links are both useful, but ultimately lead me to conclude that I cannot do what I need to do without a refactor. An interface would be cleanest, but for the sake of helping an IDE suggest some constants is a bit overkill I feel. I've seen Terminator, self-aware traits is how the world ends... ha – HelloPablo Apr 28 '21 at 14:03

0 Answers0