I am using symfony 3. I already created some events and they work fine. But this new event is different. I must somehow send some additional parameters to subscriber. What is the best way to do that? I created phenstalk service to perform job. The controller dispatch an event like:
$dispatcher = $this->container->get('event_dispatcher');
$dispatcher->dispatch(Events::POWERPLANT_GET_DATA, new PowerPlantEvent($this->getPowerPlantAction($id)));
Call like this will not work, cuz I am missing some parameters.
and I have action in subscriber:
public function onPowerPlantGetData(PowerPlantEvent $event, $startDate, $endDate, $measurement, $field)
How to do that?
If you event uses optional parameters, you can make them optional in the constructor.
use Symfony\Component\EventDispatcher\Event;
final class PowerPlantEvent extends Event
{
/**
* #var PowerPlantAction
*/
private $powerPlantAction;
/**
* #var \DateTime
*/
private $startDate;
/**
* #var \DateTime
*/
private $endDate;
/**
* #var int
*/
private $measurement;
/**
* #var string
*/
private $field;
public function __construct(
$powerPlantAction,
$startDate = null,
$endDate = null,
$measurement = null,
$field = null
) {
$this->powerPlantAction = $powerPlantAction;
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->measurement = $measurement;
$this->field = $field;
}
public function getPowerPlantAction()
{
return $this->powerPlantAction;
}
public function getStartDate()
{
return $this->startDate;
}
public function getEndDate()
{
return $this->endDate;
}
public function getMeasurement()
{
return $this->measurement;
}
public function getField()
{
return $this->field;
}
}
Then dispatching like this:
$this->eventDispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent($this->getPowerPlantAction($id))
);
Or:
$this->eventDispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent(
$this->getPowerPlantAction($id),
$startDate,
$endDate,
$measurement,
$field
)
);
In your subscriber just use those getters:
public function onPowerPlantGetData(PowerPlantEvent $powerPlantEvent)
{
$powerPlantEvent->getStartDate();
// ...
}
You should put all necessary data into Event itself and then you can access them in subscriber
For simplicity I made properties public and added all to __construct but it's up to you how you implement it. You might want to create new Event class for that
class PowerPlantEvent extends Event
{
/**
* #var \DateTime
*/
public $startDate;
/**
* #var \DateTime
*/
public $endDate;
/**
* #var Something
*/
public $measurement;
public $field;
/**
* PowerPlantEvent constructor.
*
* #param \DateTime $startDate
* #param \DateTime $endDate
* #param Something $measurement
* #param $field
*/
public function __construct(\DateTime $startDate, \DateTime $endDate, Something $measurement, $field)
{
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->measurement = $measurement;
$this->field = $field;
}
}
and call it with
$dispatcher = $this->container->get('event_dispatcher');
$dispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent($startDate, $endDate, $measurement, $field)
);
and access in subscriber (or event listener)
public function onPowerPlantGetData(PowerPlantEvent $event)
{
$startDate = $event->startDate; // you might want to implement getters for those
}
Related
I am building my own CMS system. And i did a lot of work with symfony in the past, now i wanna do all a bit more pro :) I want to render basic controller for admin dashboard witch contains a menu, systemnotifications on route "/admin" and then i want to set another controller for example "test" on route "/admin/test" and my problem is that all object notifications from controller named AdminController are not available in this second route "/admin/test", only on route "/admin"
Here my adminControler controller:
class AdminController extends Controller
{
/**
* #Route("/admin", name="adminDashboard")
*/
public function adminDashboard()
{
$loggedUser = $this->getUser()->getId();
$systemnotifications = $this->forward('App\Controller\SystemNotificationController::notif', [
'loggedUser' => $loggedUser
]);
return $this->render('admin/index.html.twig', [
'systemnotifications' => $systemnotifications
]);
}
}
Here my test controller:
class TestController extends Controller
{
/**
* #Route("/admin/test", name="test")
*/
public function test()
{
return $this->render('admin/dashboard/index.html.twig', [
]);
}
}
In twig is set, that adminController extends base.html.twig, and Test controller extends index.html.twig (this one witch is rendered from adminController.
My question is how to handle it properly with Symfony best practice. How i should set the Admin Controller for get systemnotifications object where is another Test Controller launched ?
Please help :)
There are two ways to do that the first is inyect in the twig the variable. example see this doc.
# config/packages/twig.yaml
twig:
# ...
globals:
# the value is the service's id
user_management: '#App\DataProvider\UserDataProvider'
# config/services.yaml
services:
'App\DataProvider\UserDataProvider':
arguments:
- '#session'
autoconfigure: false
The other way is more complicate, if you for example wants the responsability to render specific part of the page, like ... the barnav or the messages
Add this piece of code to the default twig:
{% block user_control %}
{{ render(controller('LayoutCoreBundle:User:index')) }}
{% endblock %}
<?php
namespace App\Controller;
use App\Event\ShowUserEvent;
use App\Event\ThemeEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use \stdClass;
class UserController extends EmitterController
{
/**
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction()
{
if (!$this->getDispatcher()->hasListeners(ThemeEvents::THEME_NAVBAR_USER)) {
return new Response();
}
/** #var ShowUserEvent $userEvent */
$userEvent = $this->triggerMethod(ThemeEvents::THEME_NAVBAR_USER, new ShowUserEvent());
$userClass = $userEvent->getUser();
$user = new stdClass();
$user->id = $userClass->getIdentifier();
$user->idEmployee = $userClass->getIdEmployee();
$user->setCompanyLogo = $userClass->getCompanyLogo();
$user->companyName = $userClass->getCompanyName();
$user->company = $userClass->getCompany();
$user->avatar = $userClass->getAvatar();
$user->fullName = $userClass->getName();
$user->menu = $userClass->getMenu();
$user->role = $userClass->getRolname();
return $this->render(
'header/index.html.twig',
[
'userJson' => $user,
]
);
}
}
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class EmitterController extends AbstractController
{
/**
* #var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* #param EventDispatcherInterface $dispatcher
*/
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->eventDispatcher = $dispatcher;
}
/**
* #return EventDispatcherInterface
*/
protected function getDispatcher()
{
return $this->eventDispatcher;
}
/**
* #param string $eventName
*
* #return bool
*/
protected function hasListener($eventName)
{
return $this->getDispatcher()->hasListeners($eventName);
}
/**
* Will look for a method of the format "on<CamelizedEventName>" and call it with the event as argument.
*
*
* Then it will dispatch the event as normal via the event dispatcher.
*
* #param $eventName
* #param Event $event
*
* #return Event
*/
protected function triggerMethod($eventName, Event $event)
{
$method = sprintf('on%s', Container::camelize(str_replace('.', '_', $eventName)));
if (is_callable([$this, $method])) {
call_user_func_array([$this, $method], [$event]);
}
if ($event->isPropagationStopped()) {
return $event;
}
$this->getDispatcher()->dispatch($eventName, $event);
return $event;
}
}
interface ThemeEvents
{
/**
* Used to receive notification data
*/
public const THEME_NOTIFICATIONS = 'theme.notifications';
/**
* Used to receive message data
*/
public const THEME_MESSAGES = 'theme.messages';
/**
* Used to receive task data
*/
public const THEME_TASKS = 'theme.tasks';
/**
* Used to receive the current user for the navbar
*/
public const THEME_NAVBAR_USER = 'theme.navbar_user';
/**
* Used to receive breadcrumb data
*/
public const THEME_BREADCRUMB = 'theme.breadcrumb';
/**
* Used to receive the current user for the sidebar
*/
public const THEME_SIDEBAR_USER = 'theme.sidebar_user';
/**
* Used to receive the sidebar menu data
*/
public const THEME_SIDEBAR_SETUP_MENU = 'theme.sidebar_setup_menu';
}
class ShowUserEvent extends ThemeEvent
{
/**
* #var UserInterface
*/
protected $user;
/**
* #var bool
*/
protected $showProfileLink = true;
/**
* #var bool
*/
protected $showLogoutLink = true;
/**
* #var NavBarUserLink[]
*/
protected $links = [];
/**
* #param UserInterface $user
* #return ShowUserEvent
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* #return UserInterface
*/
public function getUser()
{
return $this->user;
}
/**
* #return NavBarUserLink[]
*/
public function getLinks()
{
return $this->links;
}
/**
* #param NavBarUserLink $link
* #return ShowUserEvent
*/
public function addLink(NavBarUserLink $link)
{
$this->links[] = $link;
return $this;
}
/**
* #return bool
*/
public function isShowProfileLink()
{
return $this->showProfileLink;
}
/**
* #param bool $showProfileLink
* #return ShowUserEvent
*/
public function setShowProfileLink($showProfileLink)
{
$this->showProfileLink = $showProfileLink;
return $this;
}
/**
* #return bool
*/
public function isShowLogoutLink()
{
return $this->showLogoutLink;
}
/**
* #param bool $showLogoutLink
* #return ShowUserEvent
*/
public function setShowLogoutLink($showLogoutLink)
{
$this->showLogoutLink = $showLogoutLink;
return $this;
}
}
class ThemeEvent extends Event
{
}
And then only you need a evensuscriber
class NavbarUserSubscriber implements EventSubscriberInterface
{
/**
* #var Security
*/
protected $security;
/**
* #param Security $security
*/
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* #return array
*/
public static function getSubscribedEvents(): array
{
return [
ThemeEvents::THEME_NAVBAR_USER => ['onShowUser', 100],
ThemeEvents::THEME_SIDEBAR_USER => ['onShowUser', 100],
];
}
/**
* #param ShowUserEvent $event
* #throws \Exception
*/
public function onShowUser(ShowUserEvent $event)
{
if (null === $this->security->getUser()) {
return;
}
/** #var User $user */
$user = $this->security->getUser();
$event->setUser($user);
}
}
#templates/header/index.html.twig
<script type="text/javascript">
var user = {{ userJson | json_encode() | raw }};
</script>
My database schema consists mainly of the following entities: user and auth_token. Each user can have multiple auth_token's.
The problem: when selecting the currently authenticated user in a controller, the string field saltedPasswordHash is empty (""), although a value is set in the database. Getting saltedPasswordHash in the ApiKeyAuthenticator.php works (please have a look at the two TODO comments).
For whatever reason, selecting the email (string) or created (datetime) field works. Persisting new user entities with a saltedPasswordHash or selecting any other user item works fine.
An APIKeyAuthenticator is handling the authorization. When disabling firewall and authentication, everything works as expected. I included the source files below.
I'm using PHP 7.2.15-1 with mysql Ver 15.1 Distrib 10.3.13-MariaDB.
Security/ApiKeyAuthenticator.php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
public function createToken(Request $request, $providerKey)
{
$apiKey = $request->headers->get('authToken');
if (!$apiKey) {
throw new BadCredentialsException();
// or to just skip api key authentication
// return null;
}
return new PreAuthenticatedToken(
'anon.',
$apiKey,
$providerKey
);
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
if (!$userProvider instanceof ApiKeyUserProvider) {
throw new \InvalidArgumentException(
sprintf(
'The user provider must be an instance of ApiKeyUserProvider (%s was given).',
get_class($userProvider)
)
);
}
$apiKey = $token->getCredentials();
$username = $userProvider->getUsernameForApiKey($apiKey);
if (!$username) {
// CAUTION: this message will be returned to the client
// (so don't put any un-trusted messages / error strings here)
throw new BadCredentialsException(
sprintf('API Key "%s" does not exist.', $apiKey)
);
}
$user = $userProvider->loadUserByAuthToken($apiKey);
if (!isset($user)) {
throw new BadCredentialsException(
sprintf('API Key "%s" does not exist.', $apiKey)
);
}
// TODO: HERE, THE $user->getSaltedPasswordHash() RETURNS THE CORRECT VALUE!
return new PreAuthenticatedToken(
$user, // TODO: with "new User()" instead, it works!
$apiKey,
$providerKey,
$user->getRoles()
);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new Response(
// this contains information about *why* authentication failed
// use it, or return your own message
strtr($exception->getMessageKey(), $exception->getMessageData()),
401
);
}
}
Security/ApiKeyUserProvider.php
namespace App\Security;
use App\Entity\AuthToken;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class ApiKeyUserProvider implements UserProviderInterface
{
/**
* #var EntityManagerInterface
*/
private $em;
/**
* ApiKeyUserProvider constructor.
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function getUsernameForApiKey($apiKey)
{
return $apiKey;
}
public function loadUserByUsername($username)
{
// TODO: Implement loadUserByUsername() method.
}
/**
* Auth token is used as username
*
* #param string $authToken
* #return null|UserInterface
*/
public function loadUserByAuthToken($authToken): ?UserInterface
{
if (!isset($authToken)) {
return null;
}
$token = $this->em
->getRepository(AuthToken::class)
->findOneBy(['id' => AuthToken::hex2dec($authToken)]);
if (!isset($token)) {
return null;
}
return $token->getUser();
}
public
function refreshUser(UserInterface $user)
{
// this is used for storing authentication in the session
// but in this example, the token is sent in each request,
// so authentication can be stateless. Throwing this exception
// is proper to make things stateless
throw new UnsupportedUserException();
}
public
function supportsClass($class)
{
return User::class === $class;
}
}
Entity/User.php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface, EquatableInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* This needs to be nullable because the email includes the id of newly created users, which can only be obtained after inserting the new record.
* #ORM\Column(type="string", length=255, nullable=true, unique=true)
* #Assert\Length(max=255)
* #Assert\NotBlank()
*/
private $email;
/**
* Set null to disable login
* #ORM\Column(type="string", length=255, nullable=true)
* #Assert\Length(max=255)
* #Assert\NotBlank()
*/
private $saltedPasswordHash;
/**
* #ORM\Column(type="datetime")
*/
private $created;
// ...
/**
* #ORM\OneToMany(targetEntity="App\Entity\AuthToken", mappedBy="user", fetch="LAZY")
*/
private $authTokens;
/**
* #ORM\Column(type="string", length=5)
*/
private $role;
// ...
public function __construct()
{
$this->role = 'user';
$this->saltedPasswordHash = null;
}
public function getId()
{
return $this->id;
}
public function getSaltedPasswordHash(): ?string
{
return $this->saltedPasswordHash;
}
public function setSaltedPasswordHash(?string $saltedPasswordHash): self
{
$this->saltedPasswordHash = $saltedPasswordHash;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(?string $email): self
{
$this->email = $email;
return $this;
}
public function getCreated(): ?\DateTimeInterface
{
return $this->created;
}
public function setCreated(\DateTimeInterface $created): self
{
$this->created = $created;
return $this;
}
/**
* #return ArrayCollection
*/
public function getAuthTokens()
{
return $this->authTokens;
}
/**
* #param ArrayCollection $authTokens
* #return User
*/
public function setAuthTokens(ArrayCollection $authTokens): User
{
$this->authTokens = $authTokens;
return $this;
}
/**
* #param AuthToken $authToken
* #return User
*/
public function addAuthToken(AuthToken $authToken): User
{
$this->authTokens->add($authToken);
return $this;
}
/**
* #param AuthToken $authToken
* #return User
*/
public function removeAuthToken(AuthToken $authToken): User
{
$this->authTokens->removeElement($authToken);
return $this;
}
// ...
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword()
{
return $this->getSaltedPasswordHash();
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// TODO: Implement getSalt() method.
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->getEmail();
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
$this->setSaltedPasswordHash('');
}
/**
* #return mixed
*/
public function getRole()
{
return $this->role;
}
/**
* #param mixed $role
* #return User
*/
public function setRole($role): User
{
$this->role = $role;
return $this;
}
/**
* #return string
*/
public function __toString()
{
return "User " . $this->email;
}
/**
* The equality comparison should neither be done by referential equality
* nor by comparing identities (i.e. getId() === getId()).
*
* However, you do not need to compare every attribute, but only those that
* are relevant for assessing whether re-authentication is required.
*
* Also implementation should consider that $user instance may implement
* the extended user interface `AdvancedUserInterface`.
*
* https://stackoverflow.com/a/39884792/6144818
*
* #param UserInterface $user
* #return bool
*/
public function isEqualTo(UserInterface $user)
{
return (
$this->getUsername() == $user->getUsername()
) && (
$this->getRoles() == $user->getRoles()
);
}
// ...
}
Entitiy/AuthToken.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\AuthTokenRepository")
*/
class AuthToken
{
/**
* #ORM\Id()
* #ORM\Column(type="decimal", precision=32, scale=0, options={"unsigned": true})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="authTokens")
*/
private $user;
/**
* #ORM\Column(type="datetime")
*/
private $added;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $lastSeen;
/**
* #ORM\Column(type="string", length=12, nullable=true)
*/
private $apiVersion;
/**
* #return string
*/
public function getId(): string
{
return $this->id;
}
/**
* #return string
*/
public function getHexId(): string
{
return $this->dec2hex($this->id);
}
/**
* #param mixed $id
*/
public function setId($id): void
{
$this->id = $id;
}
/**
* #param mixed $id
* #throws \Exception
*/
public function generateId(): void
{
$length = 32;
$str = "";
$characters = range('0', '9');
$max = count($characters) - 1;
for ($i = 0; $i < $length; $i++) {
$rand = random_int(0, $max);
$str .= $characters[$rand];
}
$this->id = $str;
}
/**
* #return mixed
*/
public function getUser()
{
return $this->user;
}
/**
* #param mixed $user
*/
public function setUser($user): void
{
$this->user = $user;
}
/**
* #return mixed
*/
public function getAdded()
{
return $this->added;
}
/**
* #param mixed $added
*/
public function setAdded($added): void
{
$this->added = $added;
}
/**
* #return mixed
*/
public function getLastSeen()
{
return $this->lastSeen;
}
/**
* #param mixed $lastSeen
*/
public function setLastSeen($lastSeen): void
{
$this->lastSeen = $lastSeen;
}
public function getApiVersion(): ?string
{
return $this->apiVersion;
}
public function setApiVersion(string $apiVersion): self
{
$this->apiVersion = $apiVersion;
return $this;
}
public static function dec2hex(string $dec): string
{
$hex = '';
do {
$last = bcmod($dec, 16);
$hex = dechex($last) . $hex;
$dec = bcdiv(bcsub($dec, $last), 16);
} while ($dec > 0);
return $hex;
}
public static function hex2dec($hex)
{
$dec = '0';
$len = strlen($hex);
for ($i = 1; $i <= $len; $i++)
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
return $dec;
}
/**
* #return string
*/
public function __toString()
{
return "AuthToken " . $this->id . " (" . $this->user . ")";
}
}
This function of your Entity/User.php is the reason of your behavior:
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
$this->setSaltedPasswordHash('');
}
When authentication comes in play on Symfony, after the authentication, the AuthenticationProviderManager would call that eraseCredentials function, so it doesn't leak sensible information, or even worse, the sensible information does not end up in your session.
Just try to comment the setter in that function and you should have what you expect.
I need to get maximum ID of a table inside of symfony 2.7 entity. But instead of having the id I'm getting this issue.
Notice: Undefined property: AppBundle\Entity\BlogPost::$container
This is my BlogPost entity,
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* BlogPost
*
* #ORM\Table()
* #ORM\Entity
*/
class BlogPost {
const SERVER_PATH_TO_IMAGE_FOLDER = '/uploads';
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #var string
*
* #ORM\Column(name="filename", type="text")
*/
private $filename;
/**
* Set filename
*
* #param string $filename
* #return BlogPost
*/
public function setFilename($filename) {
$this->filename = $filename;
return $this;
}
public function setUploader(UploadedFile $file) {
$em = $this->container->get('doctrine.orm.entity_manager');
$highest_id = $em->createQueryBuilder()
->select('MAX(b.id)')
->from('AppBundle:BlogPost', 'b')
->getQuery()
->getSingleScalarResult();
var_dump($highest_id);
exit();// exit for check value
$url = 'uploads/events';
$file_name = 'fsdf.' . $file->guessExtension();
$file->move($url, $file_name);
}
/**
* Get filename
*
* #return string
*/
public function getFilename() {
return $this->filename;
}
/**
* #var boolean
*
* #ORM\Column(name="draft", type="boolean")
*/
private $draft;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return BlogPost
*/
public function setTitle($title) {
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle() {
return $this->title;
}
/**
* Set body
*
* #param string $body
* #return BlogPost
*/
public function setBody($body) {
$this->body = $body;
return $this;
}
/**
* Get body
*
* #return string
*/
public function getBody() {
return $this->body;
}
/**
* Set draft
*
* #param boolean $draft
* #return BlogPost
*/
public function setDraft($draft) {
$this->draft = $draft;
return $this;
}
/**
* Get draft
*
* #return boolean
*/
public function getDraft() {
return $this->draft;
}
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="blogPosts")
*/
private $category;
public function setCategory(Category $category) {
$this->category = $category;
}
public function getCategory() {
return $this->category;
}
/**
* Unmapped property to handle file uploads
*/
public $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null) {
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile() {
return $this->file;
}
/**
* Manages the copying of the file to the relevant place on the server
*/
public function upload() {
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// we use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and target filename as params
$this->getFile()->move(
self::SERVER_PATH_TO_IMAGE_FOLDER, $this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->filename = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->setFile(null);
}
/**
* Lifecycle callback to upload the file to the server
*/
public function lifecycleFileUpload() {
$this->upload();
}
/**
* Updates the hash value to force the preUpdate and postUpdate events to fire
*/
public function refreshUpdated() {
// $this->setUpdated(new \DateTime());
}
// ... the rest of your class lives under here, including the generated fields
// such as filename and updated
}
This is the part I'm trying to get max id,
public function setUploader(UploadedFile $file) {
$em = $this->container->get('doctrine.orm.entity_manager');
$highest_id = $em->createQueryBuilder()
->select('MAX(b.id)')
->from('AppBundle:BlogPost', 'b')
->getQuery()
->getSingleScalarResult();
var_dump($highest_id);
exit();// exit for check value
$url = 'uploads/events';
$file_name = 'fsdf.' . $file->guessExtension();
$file->move($url, $file_name);
}
With the help of comments and few research I figured out the issue is in 'entity_manager' part. Is there a way to call doctrine queries inside Entity?
If I were you, i would do something like that :
Controller:
$blogPost= new BlogPost () ;
$em = $this->getDoctrine()->getManager();
//..your code
// I assume you want to do that after a form post
$blogPost = $form->getData();
$id = $em->getRepository('AppBundle:BlogPost')->getMaxId();
$blogPost->setUploader($id);
//...
Repository:
public function getMaxId()
{
$qb = $this->createQueryBuilder('u');
$qb->select('u, MAX(id) as idMax');
return $qb->getQuery()->getSingleResult();
}
Entity:
public function setUploader(UploadedFile $file, $id)
{
var_dump($id);
$url = 'uploads/events';
$file_name = 'fsdf.'.$id.$file->guessExtension();
$file->move($url, $file_name);
}
It should work
I would suggest to hold the EntityManager as a class variable inside your Entity and either instantiate it via the constructor or via a setter-method which you call before you use the setUploader function.
This should be the cleanest and best readable solution.
Form another thread I found this and it's working for me.
global $kernel;
if ( 'AppCache' == get_class($kernel) )
{
$kernel = $kernel->getKernel();
}
$em = $kernel->getContainer()->get( 'doctrine.orm.entity_manager');
I can't use it in controller cause I have an admin class instead of controller on this. Since lot of people suggesting this using entity_manager inside entity is not good practice I'm using the code inside admin class instead of entity.
This is the original thread..
How to use entityManager inside Entity?
Try this in your Controller:
$blogPost= new BlogPost () ;
$em = $this->getDoctrine()->getManager();
$blogPost = $form->getData();
$maxId = $em->getRepository('AppBundle:BlogPost')->createQueryBuilder('b')
->where("MAX(b.id) as maxId")
->getQuery()
->getSingleResult();
I'm trying to implement the hwioauthbundle for connecting with Google.
However, I'm facing the problem that symfony can't seem to find the method declared in the User entity - I believe it has something to do with the FOSUserBundle that I'm also using.
Here is my GoogleProvider.php:
<?php
namespace AppBundle\Security\User\Provider;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
use Symfony\Component\Security\Core\User\UserInterface;
class GoogleProvider extends BaseClass
{
/**
* {#inheritDoc}
*/
public function connect(UserInterface $user, UserResponseInterface $response)
{
$property = $this->getProperty($response);
$username = $response->getUsername();
//on connect - get the access token and the user ID
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
//we "disconnect" previously connected users
if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
$previousUser->$setter_id(null);
$previousUser->$setter_token(null);
$this->userManager->updateUser($previousUser);
}
//we connect current user
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
$this->userManager->updateUser($user);
}
/**
* {#inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
//when the user is registrating
if (null === $user) {
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
// create new user here
$user = $this->userManager->createUser();
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
//I have set all requested data with the user's username
//modify here with relevant data
$user->setUsername($username);
$user->setEmail($username);
$user->setPassword($username);
$user->setEnabled(true);
$this->userManager->updateUser($user);
return $user;
}
//if user exists - go with the HWIOAuth way
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
//update access token
$user->$setter($response->getAccessToken());
return $user;
}
}
And here is FOSUBUserProvider.php:
<?php
namespace HWI\Bundle\OAuthBundle\Security\Core\User;
use FOS\UserBundle\Model\User;
use FOS\UserBundle\Model\UserManagerInterface;
use HWI\Bundle\OAuthBundle\Connect\AccountConnectorInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class FOSUBUserProvider implements UserProviderInterface, AccountConnectorInterface, OAuthAwareUserProviderInterface
{
/**
* #var UserManagerInterface
*/
protected $userManager;
/**
* #var array
*/
protected $properties = array(
'identifier' => 'id',
);
/**
* #var PropertyAccessor
*/
protected $accessor;
/**
* Constructor.
*
* #param UserManagerInterface $userManager FOSUB user provider.
* #param array $properties Property mapping.
*/
public function __construct(UserManagerInterface $userManager, array $properties)
{
$this->userManager = $userManager;
$this->properties = array_merge($this->properties, $properties);
$this->accessor = PropertyAccess::createPropertyAccessor();
}
/**
* {#inheritDoc}
*/
public function loadUserByUsername($username)
{
// Compatibility with FOSUserBundle < 2.0
if (class_exists('FOS\UserBundle\Form\Handler\RegistrationFormHandler')) {
return $this->userManager->loadUserByUsername($username);
}
return $this->userManager->findUserByUsername($username);
}
/**
* {#inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
if (null === $user || null === $username) {
throw new AccountNotLinkedException(sprintf("User '%s' not found.", $username));
}
return $user;
}
/**
* {#inheritDoc}
*/
public function connect(UserInterface $user, UserResponseInterface $response)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Expected an instance of FOS\UserBundle\Model\User, but got "%s".', get_class($user)));
}
$property = $this->getProperty($response);
if (!$this->accessor->isWritable($user, $property)) {
throw new \RuntimeException(sprintf("Class '%s' must have defined setter method for property: '%s'.", get_class($user), $property));
}
$username = $response->getUsername();
if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
$this->accessor->setValue($previousUser, $property, null);
$this->userManager->updateUser($previousUser);
}
$this->accessor->setValue($user, $property, $username);
$this->userManager->updateUser($user);
}
/**
* {#inheritDoc}
*/
public function refreshUser(UserInterface $user)
{
// Compatibility with FOSUserBundle < 2.0
if (class_exists('FOS\UserBundle\Form\Handler\RegistrationFormHandler')) {
return $this->userManager->refreshUser($user);
}
$identifier = $this->properties['identifier'];
if (!$user instanceof User || !$this->accessor->isReadable($user, $identifier)) {
throw new UnsupportedUserException(sprintf('Expected an instance of FOS\UserBundle\Model\User, but got "%s".', get_class($user)));
}
$userId = $this->accessor->getValue($user, $identifier);
if (null === $user = $this->userManager->findUserBy(array($identifier => $userId))) {
throw new UsernameNotFoundException(sprintf('User with ID "%d" could not be reloaded.', $userId));
}
return $user;
}
/**
* {#inheritDoc}
*/
public function supportsClass($class)
{
$userClass = $this->userManager->getClass();
return $userClass === $class || is_subclass_of($class, $userClass);
}
/**
* Gets the property for the response.
*
* #param UserResponseInterface $response
*
* #return string
*
* #throws \RuntimeException
*/
protected function getProperty(UserResponseInterface $response)
{
$resourceOwnerName = $response->getResourceOwner()->getName();
if (!isset($this->properties[$resourceOwnerName])) {
throw new \RuntimeException(sprintf("No property defined for entity for resource owner '%s'.", $resourceOwnerName));
}
return $this->properties[$resourceOwnerName];
}
}
And here is my service:
parameters:
my_user_provider.class: AppBundle\Security\User\Provider\GoogleProvider
services:
my_user_provider:
class: "%my_user_provider.class%"
#this is the place where the properties are passed to the UserProvider - see config.yml
arguments: [#fos_user.user_manager,{facebook: facebook_id, google: google_id}]
Here is my User entity:
<?php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*/
private $name;
/**
* #var integer
*/
private $facebook_id;
/**
* #var string
*/
private $facebookAccessToken;
/**
* #var integer
*/
private $google_id;
/**
* #var string
*/
private $googleAccessToken;
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $keyword;
/**
* Constructor
*/
public function __construct()
{
$this->keyword = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set facebook_id
*
* #param integer $facebookId
* #return User
*/
public function setFacebookId($facebookId)
{
$this->facebook_id = $facebookId;
return $this;
}
/**
* Get facebook_id
*
* #return integer
*/
public function getFacebookId()
{
return $this->facebook_id;
}
/**
* Set facebookAccessToken
*
* #param string $facebookAccessToken
* #return User
*/
public function setFacebookAccessToken($facebookAccessToken)
{
$this->facebookAccessToken = $facebookAccessToken;
return $this;
}
/**
* Get facebookAccessToken
*
* #return string
*/
public function getFacebookAccessToken()
{
return $this->facebookAccessToken;
}
/**
* Set google_id
*
* #param integer $googleId
* #return User
*/
public function setGoogleId($googleId)
{
$this->google_id = $googleId;
return $this;
}
/**
* Get google_id
*
* #return integer
*/
public function getGoogleId()
{
return $this->google_id;
}
/**
* Set googleAccessToken
*
* #param string $googleAccessToken
* #return User
*/
public function setGoogleAccessToken($googleAccessToken)
{
$this->googleAccessToken = $googleAccessToken;
return $this;
}
/**
* Get googleAccessToken
*
* #return string
*/
public function getGoogleAccessToken()
{
return $this->googleAccessToken;
}
/**
* Add keyword
*
* #param \AppBundle\Entity\Keyword $keyword
* #return User
*/
public function addKeyword(\AppBundle\Entity\Keyword $keyword)
{
$this->keyword[] = $keyword;
return $this;
}
/**
* Remove keyword
*
* #param \AppBundle\Entity\Keyword $keyword
*/
public function removeKeyword(\AppBundle\Entity\Keyword $keyword)
{
$this->keyword->removeElement($keyword);
}
/**
* Get keyword
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getKeyword()
{
return $this->keyword;
}
}
Does anyone have any idea what I'm doing wrong?
I works when I do:
$user->setUsername($username);
$user->setEmail($username);
$user->setPassword($username);
$user->setEnabled(true);
But for instance I can't do:
$user->setGoogleId(123);
but I guess it is because they are not in my User entity, but in FOSUserBundle.
It seems like it doesn't extend my User entity.
I appreciate all kinds of help!
You need to add every method you expect to work in your User entity. There is no magic. So, add a setFacebookId(), setFacebookAccessToken(), etc. Indeed, you could even add a setGoogleId(), but there is no property called $googleId in your entity.
I've generated the form through entity generator,
I've extended the Cobrand Controller
class CobrandController extends Controller
{
public function createAction()
{
// the key used to lookup the template
$templateKey = 'edit';
if (false === $this->admin->isGranted('CREATE')) {
throw new AccessDeniedException();
}
$object = $this->admin->getNewInstance();
$this->admin->setSubject($object);
/** #var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
$form->setData($object);
if ($this->getRestMethod()== 'POST') {
// $cobrand = new Cobrand();
$em=$this->getDoctrine()->getManager();
$cobrand=new Cobrand($em);
$form->bind($this->get('request'));
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
$this->admin->create($object);
if ($this->isXmlHttpRequest()) {
return $this->renderJson(array(
'result' => 'ok',
'objectId' => $this->admin->getNormalizedIdentifier($object)
));
}
$this->addFlash('sonata_flash_success','flash_create_success');
// redirect to edit mode
return $this->redirectTo($object);
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash('sonata_flash_error', 'flash_create_error');
}
} elseif ($this->isPreviewRequested()) {
// pick the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$view = $form->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($view, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), array(
'action' => 'create',
'form' => $view,
'object' => $object,
));
}
}
Here's my entity code,
class Cobrand
{
/**
* #var \DateTime
*/
private $created;
/**
* #var \DateTime
*/
private $updated;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $code;
/**
* #var string
*/
private $logo;
/**
* #var boolean
*/
private $cobrandedProductsOnly;
/**
* #var integer
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $productPrices;
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $promocodes;
/**
* Constructor
*/
public function __construct()
{
$this->productPrices = new \Doctrine\Common\Collections\ArrayCollection();
$this->promocodes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Set created
*
* #param \DateTime $created
* #return Cobrand
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param \DateTime $updated
* #return Cobrand
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set name
*
* #param string $name
* #return Cobrand
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set code
*
* #param string $code
* #return Cobrand
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
}
User will enter the values on the form and I wan't to access those values in the createAction method of controller.
I want to change the value of $name in the controller.
How to access this ?
Thanks,
Faisal Nasir
Try this after binding request
$form->get('name')->setData('new value');