У меня есть служба, которую я определил как глобальную переменную веточки, которая использует автоматическое подключение TokenStorageInterface, чтобы получить текущего пользователя, вошедшего в систему.
Иногда токен равен нулю и выдает исключение при попытке доступа к нулевому объекту. Call to a member function getUser() on null
Это код barebone, который ломается.
BonusService.php
namespace AppBundle\Service;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class BonusService {
private $user;
private $manager;
__construct(TokenStorageInterface, $tokenStorage, ObjectManager $manager) {
$this->user = $tokenStorage->getToken()->getUser(); // Sometimes fails here
$this->manager = $manager;
}
public function hasBonuses() {
return count($this->manager->getRepository(Bonus::class)->findBy(array('contact' => $user)) > 0;
}
}
services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: true
AppBundle\Service\BonusService:
config.yml
twig:
...
globals:
bonus_service: '@AppBundle\Service\BonusService'
index.html.twig
...
{% if bonus_service.hasBonuses %}Have Bonuses{% endif %}
...
Я искал причины, по которым хранилище токенов может быть нулевым, когда веточка делает это. В основном возникла одна проблема - убедиться, что мой маршрут находится за брандмауэром, который в данном случае и требует аутентифицированного пользователя.
Также следует отметить, что у меня есть аналогичная служба с идентичным конструктором, который используется в контроллере. Когда BonusService не решает использовать нулевой токен и страница загружается, у другой службы нет проблем с захватом токена. Когда я удаляю вызов службы в twig, страница загружается в 100% случаев, даже с другой службой и идентичным конструктором.
Любая помощь приветствуется!
@Eakethet в этом случае пользователь вошел в систему. (Обычно у меня токен окружен предложением is_null(), но для краткости я не включил его во фрагмент)
Вы должны сохранить ссылку на хранилище токенов, а не вводить пользователя в конструктор. Токен может по-прежнему иметь значение null, но приложение находится в двух разных состояниях - когда конструктор вызывается, контейнер все еще загружается.
Или вы можете избежать сохранения пользователя в службе и сделать что-то вроде Bonus_service.hasBonuses, {user: app.user}
пожалуйста, никогда и никогда не меняйте код (кроме затенения переменных / паролей и т. д.), который вы публикуете здесь. Удаление is_null меняет все.
Спасибо @vstm! Мне удалось заставить его работать, назначив tokenStorage свойству класса и создав метод getUser() для выборки пользователя при необходимости. Не могли бы вы сделать свой комментарий ответом, чтобы я мог его принять?
@Eakethet Я думал об этом, однако могут быть случаи, когда я хотел бы повторно использовать эту службу в других ситуациях, которые могут выходить за пределы ветки. Я бы предпочел, чтобы это было автоматически подключено к службе, чем придумывать другие беспорядочные способы поиска пользователя в других контекстах.
@SteppingHat например. вы хотите знать, если у кого-то еще есть бонусы, а не у пользователя, который на самом деле вошел в систему, поэтому для меня это выглядит более многоразовым, когда вы передаете User в качестве параметра, но я не знаю вашего бизнес-случая
@Eakethet ах да, я понимаю, что вы имеете в виду. Но да, в этом случае это всегда будет для текущего пользователя, вошедшего в систему, поскольку есть вспомогательные функции, которые называются специфичными для пользователей, вошедших в систему.




При создании сервисов ваш конструктор должен избегать выполнения чего-то большего, кроме сохранения внедренных сервисов в качестве ссылки.
class BonusService {
private $tokenStorage;
private $manager;
public function __construct(TokenStorageInterface $tokenStorage, ObjectManager $manager) {
$this->tokenStorage = $tokenStorage;
$this->manager = $manager;
}
public function hasBonuses() {
if (!$this-tokenStorage->getToken() instanceof User) {
return false;
}
return count($this->manager->getRepository(Bonus::class)->findBy(array(
'contact' => $this-tokenStorage->getToken()->getUser())
) > 0;
}
}
Вам все равно нужно проверить, установлен ли токен и является ли он экземпляром User (или, как бы то ни было, вызывается ваш User).
Причина, по которой вы не должны использовать какие-либо внедренные службы в конструкторе, заключается в том, что на этом этапе контейнер все еще загружается и строит все службы. Таким образом, ваши зависимости могут быть еще не полностью инициализированы.
Когда пользователь не вошел в систему, он даст вам null