Jump to content

How should importing classes be used when using traits?


NotionCommotion

Recommended Posts

Haven't really used traits much and am confused whether to import other classes in the trait or the class that uses the trait (might also be an annotation question but not sure).

Must import statements (i.e. use Foo\Bar;) be included in the class or can they be included in just the trait which is used by a class?  Does it depend and if so on what?  Does having one class use another class impact the decision?  Is it okay for one trait to use another and another trait to just use the first trait or should it use both?

What is odd is I apparently must (to not get an error) import ApiProperty to the trait but doing so is not required for the class, and must import Groups, SerializedName, and Mapping in the class but doing so is not required for the trait.

Instead of just a yes/no answer, would appreciate an explanation why it is what it is.

Thanks!

<?php

namespace App\Entity\Document;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\MultiTenenacy\PublicIdTenantTrait;

// Must Groups, SerializedName, Owner be imported in this class or just PublicIdTenantTrait 
// use Symfony\Component\Serializer\Annotation\Groups;
// use Symfony\Component\Serializer\Annotation\SerializedName;use 
// use App\Entity\Owner\Owner;

/**
 * @ORM\Entity()
 * @ApiResource()
 */
class Document
{
    use PublicIdTenantTrait;
}

 

<?php
namespace App\Entity\MultiTenenacy;

use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;

trait PublicIdTenantTrait
{
    use PublicIdExtendedTenantTrait;
    
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ApiProperty(identifier=false)
     * @ORM\Column(type="integer")
     */
    private $id;

    public function getId(): ?int
    {
        return $this->id;
    }
}

 

<?php
namespace App\Entity\MultiTenenacy;

use ApiPlatform\Core\Annotation\ApiProperty;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Doctrine\ORM\Mapping as ORM;

trait PublicIdExtendedTenantTrait
{
    use TenantTrait;

    /**
     * @ORM\Column(type="integer")
     * @ApiProperty(identifier=true)
     * @Groups({"tenant_entity:read"})
     * @SerializedName("id")
     */
    private $publicId;

    public function getPublicId(): ?int
    {
        return $this->publicId;
    }

    public function setPublicId(int $publicId): PublicIdTenantInterface
    {
        $this->publicId = $publicId;

        return $this;
    }

    public function generatePublicId(): PublicIdTenantInterface
    {
        $this->getOwner()->setPublicId($this);

        return $this;
    }

    public function getPublicIdTag(): string
    {
        return lcfirst((new \ReflectionClass($this))->getShortName()).'Id';
    }
}

 

<?php
namespace App\Entity\MultiTenenacy;

use Doctrine\ORM\Mapping as ORM;
use App\Entity\Owner\Owner;

trait TenantTrait
{
    /**
     * inversedBy performed in child where required
     * @ORM\ManyToOne(targetEntity=Owner::class)
     * @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
     */
    private $owner;

    public function getOwner(): ?Owner
    {
        return $this->owner;
    }

    public function setOwner(?Owner $owner): TenantInterface
    {
        $this->owner = $owner;

        return $this;
    }
}

 

Link to comment
Share on other sites

18 hours ago, requinix said:

This is a "try it and find out" situation.

If you've tried it and discovered that the use statements in the trait's file can be picked up by the annotation parser then there's your answer.

Thanks requinix,

Appears that imports for non-annotation purposes (i.e. type declaration, creating a new object) should be located in the file where they are being used, right?

For annotation purposes, sounds like it is based on how the given application's author elected to write the application, true?  Is there any approach that is more typical than the other?  Does it make sense to always import in both the trait and the class?

Do you know whether PHP 8's attributes are independent of the application?

 

Link to comment
Share on other sites

7 hours ago, NotionCommotion said:

Appears that imports for non-annotation purposes (i.e. type declaration, creating a new object) should be located in the file where they are being used, right?

As far as PHP is concerned, yeah. That's how they work. It's what they do. It wouldn't make sense to put them in other files.

 

7 hours ago, NotionCommotion said:

For annotation purposes, sounds like it is based on how the given application's author elected to write the application, true?  Is there any approach that is more typical than the other?  Does it make sense to always import in both the trait and the class?

If use statements are supported for annotations (they are) and if the parser is smart enough to grab those from the trait's definition rather than the class using the trait (apparently it is) then that means use statements for annotations work the same way as regular use statements.

There is no question about "approaches". Use statements do what they do for code and also annotations. You can make use of them or not, and putting them in other files where they won't do anything is pointless.

 

7 hours ago, NotionCommotion said:

Do you know whether PHP 8's attributes are independent of the application?

What "application"?

Link to comment
Share on other sites

On 6/13/2021 at 4:31 PM, requinix said:

There is no question about "approaches". Use statements do what they do for code and also annotations. You can make use of them or not, and putting them in other files where they won't do anything is pointless.

What "application"?


The "application" I was referring to is Symfony and/or Doctrine.

Take the following class which uses the traits posted in my original post:

<?php

namespace App\Entity\MultiTenenacy;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
use App\Entity\Owner\Owner;

/**
 * @ORM\Entity()
 */
class Test
{
    use PublicIdTenantTrait;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

}

When checking the schema, all validates.  But then I comment out the use input statements for Groups, SerializedName, and Owner, and get an error that I didn't use a "use" statement for Groups.  I add back Groups, but then get it for SerializedName.  I add back SerializedName and then get it for Owner.  Nowhere in the Test class am I using any of these classes and only in the traits that I do, but still I get these errors.

[michael@devserver api2]$ bin/console  doctrine:schema:validate

In AnnotationException.php line 39:

  [Semantical Error] The annotation "@Groups" in property App\Entity\MultiTenenacy\Test::$publicId was never imported. Did you maybe forget to add a "use" statement for this annotation?


[michael@devserver api2]$ bin/console  doctrine:schema:validate

In AnnotationException.php line 39:

  [Semantical Error] The annotation "@SerializedName" in property App\Entity\MultiTenenacy\Test::$publicId was never imported. Did you maybe forget to add a "use" statement for this annotation?


[michael@devserver api2]$ bin/console  doctrine:schema:validate

Mapping
-------


In MappingException.php line 870:

  The target-entity App\Entity\MultiTenenacy\Owner cannot be found in 'App\Entity\MultiTenenacy\Test#owner'.


doctrine:schema:validate [--em EM] [--skip-mapping] [--skip-sync]

[michael@devserver api2]$


So, then I put them back in the Test class, and remove them from the traits, and have no errors.  And then I remove the import for ApiProperty in the trait and get an error.

[michael@devserver api2]$ bin/console  doctrine:schema:validate

In AnnotationException.php line 39:

  [Semantical Error] The annotation "@ApiProperty" in property App\Entity\MultiTenenacy\Test::$id was never imported. Did you maybe forget to add a "use" statement for this annotation?


[michael@devserver api2]$

I then add ApiProperty to the Test class and then no errors.  I see no pattern and am discovering these issues by just trial and error, but there has to be a reason, no?

 

Link to comment
Share on other sites

2 hours ago, requinix said:

Are you saying that in one situation, having the imports in the trait file and not the class file works, but in another situation the same arrangement does not work?

Yes!

When you commented about the "parser", is this PHP's C code or PHP application code part of a given framework (i.e. Doctrine, Symfony)?  If the later, I could see how one might get different results, but would not expect these two frameworks to implement it differently.

Also, you will see that the traits provided in the original post use other traits.  It is my understanding that doing so is acceptable but maybe I am not doing so correctly?

On 6/12/2021 at 11:40 AM, NotionCommotion said:

What is odd is I apparently must (to not get an error) import ApiProperty to the trait but doing so is not required for the class, and must import Groups, SerializedName, and Mapping in the class but doing so is not required for the trait.

 

Link to comment
Share on other sites

PHP does not parse /** */ docblocks. That's Doctrine/Symfony (I forget which one technically does the annotation support). But code doesn't behave randomly so there must be some difference between the situation that works and the one that does not.

Either way, the answer is the same: don't import stuff you don't need, do import stuff you do need.

Link to comment
Share on other sites

Agree, code thankfully doesn't behave randomly, only me!

Also, agree I shouldn't import stuff I don't need, but frustrating that there appears to be no way to determine whether stuff is needed other than trial and error.

Back to earlier question, you think PHP8 attributes might be more consistent (assuming the framework supports them)?  If you don't know, have what I need.  Thanks

Link to comment
Share on other sites

3 hours ago, NotionCommotion said:

Also, agree I shouldn't import stuff I don't need, but frustrating that there appears to be no way to determine whether stuff is needed other than trial and error.

There could also be a bug with annotation support. It really should not be that inconsistent - unless there's some kind of technical limitation.

 

3 hours ago, NotionCommotion said:

Back to earlier question, you think PHP8 attributes might be more consistent (assuming the framework supports them)?

Probably.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.