coopTilleuls / CoopTilleulsForgotPasswordBundle

Provides a "forgot password" complete feature for your API through a Symfony bundle
MIT License
79 stars 25 forks source link

Method setUser() is not compatible with method #117

Closed MichaelKheel closed 1 year ago

MichaelKheel commented 1 year ago

Compile Error: Declaration of App\Entity\PasswordToken::setUser(App\Entity\User $user): App\Entity\PasswordToken must be compatible with CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken::setUser($user)

The User entity was created using the standard method from the documentation. What could be the problem? Symfony 6.2.0

# config/packages/coop_tilleuls_forgot_password.yaml
coop_tilleuls_forgot_password:
    password_token:
        class: 'App\Entity\PasswordToken' # Token class fully qualified name (required)
        expires_in: '1 day'               # Token duration (optional, default value)
        user_field: 'user'                # User property in token class (optional, default value)
        serialization_groups: [ ]         # Serialization groups used in GET /forgot-password/{tokenValue} (optional, default value)
    user:
        class: 'App\Entity\User'          # User class fully qualified name (required)
        email_field: 'email'              # Email property in user class (optional, default value)
        password_field: 'password'        # Password property in user class (optional, default value)
        authorized_fields: [ 'email' ]    # User properties authorized to reset the password (optional, default value)
    use_jms_serializer: false             # Switch between symfony's serializer component or JMS Serializer
// src/Entity/PasswordToken.php
<?php
namespace App\Entity;

use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class PasswordToken extends AbstractPasswordToken
{
    #[ORM\Id]
    #[ORM\Column(type: 'integer', nullable: false)]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: User::class)]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $user = null;

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

    public function getUser(): ?User
    {
        return $this->user;
    }

    public function setUser(User $user): self
    {
        $this->user = $user;

        return $this;
    }
}
// src/Entity/User.php
<?php

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Ignore;

#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    private ?string $email = null;

    #[ORM\Column]
    private array $roles = [];

    /**
     * @var string The hashed password
     */
    #[Ignore]
    #[ORM\Column]
    private ?string $password = null;

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

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    #[Ignore]
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see PasswordAuthenticatedUserInterface
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }
}
vincentchalamon commented 1 year ago

Hi @MichaelKheel,

The AbstractPasswordToken::setUser($user) method doesn't typehint the $user argument, as it can be any class name. Your PasswordToken::setUser(User $user) method typehints it, which does not respect the inheritance of the method in PHP. You must declare it as its parent:

public function setUser($user): self
{
    $this->user = $user;

    return $this;
}

If you want to typehint the $user argument, please use the PHPDoc:

/**
 * @param User $user
 */
public function setUser($user): self
{
    $this->user = $user;

    return $this;
}

The documentation has been updated accordingly.