I am doing unit testing / functional testing, but my code gives me an error. Kindly have a look on my code and suggest a solution.
Codes On Controller
<?php
namespace App\Controller;
use App\Entity\Organization;
use App\Entity\User;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Psr\Log\LoggerInterface;
use Swift_Mailer;
use Swift_Message;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class RegisterController extends AbstractController
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Validate the data
* Encodes the password
* Creates new organization if evaplyId does not exists
* Create user and send verification email.
* return status
*
* #param Request $request
* #param UserPasswordEncoderInterface $userPasswordEncoder
* #param ValidatorInterface $validator
* #param Swift_Mailer $mailer
* #return Response
*/
public function register(Request $request, UserPasswordEncoderInterface $userPasswordEncoder, ValidatorInterface $validator, Swift_Mailer $mailer)
{
try {
$form = $request->getContent();
$form = json_decode($form);
$entityManager = $this->getDoctrine()->getManager(); //doctrine manager for entity managing
$user = new User();
$user->setEmail($form->email);
if (!$this->checkEmail($form->email)) {
return new JsonResponse(['status' => false, 'message' => 'Email already exists']);
}
$user->setFirstName($form->first_name);
$user->setLastName($form->last_name);
$user->setPassword($form->plain_password);
if (!$form->evaply_id) {
if(!$form->organization_name)
return new JsonResponse(['status' => false, 'message' => 'Missing field Organization']);
$organization = new Organization();
$organization->setName($form->organization_name);
$startGuideStatus['status'] = false;
$startGuideStatus['add_company'] = false;
$startGuideStatus['first_connection'] = false;
$startGuideStatus['first_scorecard'] = false;
$startGuideStatus['first_order'] = false;
$organization->setStartGuideStatus(($startGuideStatus));
$organization->setCustOrSupplier($form->cust_or_supplier);
$evaply_id = $this->generateEvaplyId($form->organization_name);
$organization->setEvaplyId($evaply_id);
$entityManager->persist($organization);
$entityManager->flush();
} else {
$organization = $this->getDoctrine()->getRepository(Organization::class)->findOneBy(array('evaplyId' => $form->evaply_id));
if (!$organization) {
return new JsonResponse(['status' => false, 'message' => 'Invalid Evaply ID']);
}
}
$user->setOrganization($organization);
$user->setUuid($this->generateUUID());
$user->setRoles(['Admin']);
$error = $validator->validate($user);
if (count($error) > 0) {
return new JsonResponse(['status' => false, 'message' => 'Validation error']);
}
if ($form->plain_password != $form->confirm_password) {
return new JsonResponse(['status' => false, 'message' => 'Password miss match']);
}
$user->setPassword(
$userPasswordEncoder->encodePassword(
$user,
$form->plain_password
)
);
$token = $this->generateVerificationToken($user);
$this->logger->info($token);
$verificationUrl = $this->getParameter('app_url') . "account/verify/" . $token;
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$this->sendUserRegisteredMail($user, $verificationUrl, $mailer);
return new JsonResponse(['status' => true, 'message' => "Successfully registered"]);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
return new Response($e->getMessage());
}
}
/**
* Check email duplication in database
*
* #param $email
* #return bool
*/
public function checkEmail($email)
{
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(array('email' => $email));
if ($user)
return false;
else
return true;
}
/**
* Generate a unique id using organization name and timestamp
*
* #param $orgName
* #return string
*/
public function generateEvaplyId($orgName)
{
$org = strtoupper(substr($orgName, 0, 3));
$dateTime = time();
$evaply_id = $org . $dateTime;
return $evaply_id;
}
/**
* Generate unique uuid for user
* Recursive function
*
* #return int
*/
public function generateUUID()
{
$uuid = rand(1000000000, 9999999999);
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(array('uuid' => $uuid));
if ($user) {
return $this->generateUUID();
} else {
return $uuid;
}
}
/**
* Send email to user after a successfull registartion.
*
* #param User $user
* #param Swift_Mailer $mailer
* #return int
*/
public function sendUserRegisteredMail(User $user, $verificationUrl, Swift_Mailer $mailer)
{
$message = (new Swift_Message("Successfully Registered"))
->setFrom('admin#evaply.com')
->setTo($user->getEmail())
->setBody(
$this->renderView(
'emails/registration.html.twig',
array('firstName' => $user->getFirstName(), 'lastName' => $user->getLastName(), 'verificationUrl' => $verificationUrl)
),
'text/html'
);
return $mailer->send($message);
}
/**
* Generates a verification token using aes-128-gcm encryption
* encrypts email and uuid
* encode ciphertext with base64 encoder
*
* #param User $user
* #return string
*/
public function generateVerificationToken(User $user)
{
$data = array();
$data['email'] = $user->getEmail();
$data['uuid'] = $user->getUuid();
$plaintext = json_encode($data);
$cipher = $this->getParameter('cipher');
if (in_array($cipher, openssl_get_cipher_methods())) {
$this->logger->info("inside cipher");
//$ivlen = openssl_cipher_iv_length($cipher);
$iv = $this->getParameter('secretIV');
$key = $this->getParameter('cipher_key');
//$tag = $this->getParameter('tagg');
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options = 0, $iv);
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options = 0, $iv);
return $token = base64_encode($ciphertext);
}
}
}
Codes On Tests/Controller Folder
<?php
/**
* Created by PhpStorm.
* User: xavier-phases
* Date: 22/1/19
* Time: 5:09 PM
*/
namespace App\Tests\Controller;
use App\Controller\RegisterController;
use PHPUnit\Framework\TestCase;
class EvaplyTest extends TestCase
{
public function testGenerate_evaply_clid(LoggerInterface $logger)
{
$id= new RegisterController();
$org_name=$id->generateEvaplyId('Tset');
$org = strtoupper(substr($org_name, 0, 3));
$dateTime = time();
$evaply_id = $org . $dateTime;
return $evaply_id;
}
}`
I am calling the generateEvaplyId() method from the register controller. I have installed all test packages. It gives me the following error:
"ArgumentCountError: Too few arguments to function App\Tests\Controller\EvaplyTest::testGenerate_evaply_clid(), 0 passed and exactly 1 expected". KIndly have a look and suggest me a solution.
<?php
namespace App\Tests\Controller;
use App\Controller\RegisterController;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
class EvaplyTest extends TestCase
{
/** #var LoggerInterface|MockObject */
protected $logger;
/** #var RegisterController **/
protected $controller;
/**
* {#inheritdoc}
*/
protected function setUp()
{
$this->logger = $this->createMock(LoggerInterface::class);
$this->controller = new RegisterController(
$this->logger,
);
parent::setUp();
}
public function testGenerate_evaply_clid()
{
$result = $this->controller->generateEvaplyId('Test');
$this->assertEquals( 'TEST1548303737',$result);
}
}
Here is how it should be. Initiate controller instance and all it's dependencies in setUp method, then, in all the tests you can reference them. By default, PHPUnit does not expect any dependencies for tests suite, what you tried to do is called data provider (check for data providers docs), it does require additional annotation and used for completely different purpose.
And for the last, I do NOT recommend you to store any part of logic in controllers. Move everything to service layer. Controllers are only to catch request, pass it to manager and return response.
Related
I have a Guard Authentication in my Symfony Web Application. I would like to perform some unit tests. I'm unable to simulate an authentification in my tests. The token stays null when calling $tokenStorage->getToken().
Note:
The login authentification is working under dev and prod environnement.
I saw quite a lot of related topics without success and the doc.
Symfony version: 3.4.
Reproduce: you can reproduce the error from this repo (symfony project). This repo defined one entity User with a custom constraint validator ExampleValidator. In this constraint, I need to have the current logged user.
Code sample:
After manually creating an User, the login function used in tests:
private function logIn($firewallName = 'main'){
// dummy call to bypass the hasPreviousSession check
$crawler = $this->client->request('GET', '/');
$session = $this->client->getContainer()->get('session');
/** #var User $user */
$user = $this->entityManager->getRepository(User::class)
->findOneBy(['email' => 'user1#example.com']);
// you may need to use a different token class depending on your application.
// for example, when using Guard authentication you must instantiate PostAuthenticationGuardToken
$token = new PostAuthenticationGuardToken($user, $firewallName, [new Role('ROLE_CLIENT')]);
self::$kernel->getContainer()->get('security.token_storage')->setToken($token);
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
The User call from tokenStorage (from service function):
class ExampleValidator extends ConstraintValidator{
protected $requestStack;
protected $em;
protected $user_id;
public function __construct(RequestStack $request,
EntityManager $em,
TokenStorage $tokenStorage){
$this->requestStack = $request;
$this->em = $em;
/** #var User $user */
// Token is always null
$user = $tokenStorage->getToken()->getUser();
$this->user_id = $user != "anon." ? $user->getId() : null;
}
/**
* #param $value
* #param Constraint $constraint
*/
public function validate($value, Constraint $constraint)
{
// validation rules ...
}
}
LoginFormAuthenticator.php
<?php
namespace AppBundle\Security;
use AppBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator{
use TargetPathTrait;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
private $loginAttemptRepository;
public function __construct(EntityManagerInterface $entityManager,
UrlGeneratorInterface $urlGenerator,
CsrfTokenManagerInterface $csrfTokenManager,
UserPasswordEncoderInterface $passwordEncoder){
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
/**
* #param Request $request
* #return bool
*/
public function supports(Request $request){
return $request->getPathInfo() == '/login_check' &&
$request->isMethod('POST') &&
$request->request->get('_password') !== null;
}
/**
* #param Request $request
* #return array|mixed|void|null
*/
public function getCredentials(Request $request){
$isLoginSubmit = $request->getPathInfo() == '/login_check' &&
$request->isMethod('POST') &&
$request->request->get('_password') !== null;
$isCaptcha = $request->request->get('captcha_set');
if ($isCaptcha == 1 && $request->request->get('_password') !== null) {
$secret = ...;
if($_POST['g-recaptcha-response'] !== null){
// Paramètre renvoyé par le recaptcha
$response = $_POST['g-recaptcha-response'];
$remoteip = $_SERVER['REMOTE_ADDR'];
$api_url = "https://www.google.com/recaptcha/api/siteverify?secret="
. $secret
. "&response=" . $response
. "&remoteip=" . $remoteip ;
$decode = json_decode(file_get_contents($api_url), true);
if ($decode['success'] == true) {
$username = $request->request->get('_username');
$password = $request->request->get('_password');
$csrfToken = $request->request->get('_csrf_token');
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $csrfToken))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
$request->getSession()->set(
Security::LAST_USERNAME,
$username
);
return [
'username' => $username,
'password' => $password,
];
}
else{
throw new CustomUserMessageAuthenticationException('Captcha invalids.');
}
}
else{
throw new CustomUserMessageAuthenticationException('Captcha invalids.');
}
}
else {
if (!$isLoginSubmit) {
// skip authentication
return;
}
$username = $request->request->get('_username');
$password = $request->request->get('_password');
$csrfToken = $request->request->get('_csrf_token');
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $csrfToken))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
$request->getSession()->set(
Security::LAST_USERNAME,
$username
);
return [
'username' => $username,
'password' => $password,
];
}
}
/**
* #param mixed $credentials
* #param UserProviderInterface $userProvider
* #return User|object|UserInterface|null
*/
public function getUser($credentials, UserProviderInterface $userProvider){
$username = $credentials["username"];
$user = $this->entityManager->getRepository(User::class)
->findOneBy(['username' => $username]);
return $user;
}
/**
* #param mixed $credentials
* #param UserInterface $user
* #return bool
*/
public function checkCredentials($credentials, UserInterface $user){
$password = $credentials["password"];
$rep = false;
if ($this->passwordEncoder->isPasswordValid($user, $password)){
$rep = true;
}
return $rep;
}
/**
* #param Request $request
* #param TokenInterface $token
* #param string $providerKey
* #return RedirectResponse
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey){
$targetPath = null;
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('map'));
}
/**
* #return string
*/
protected function getLoginUrl(){
return $this->urlGenerator->generate('fos_user_security_login');
}
}
I believe the root of your problem is that you are using multiple container instances. In particular, your logIn() function works on the container of the client, but the validator is from a different container that you boot up during setUp(). Thus, the changes you make in logIn() to the client container do not affect the validator you are actually testing.
Using the same container everywhere, e.g. the one from the client, should solve this. The following changes to your repository make the test pass:
diff --git a/tests/AppBundle/Validator/UserTest.php b/tests/AppBundle/Validator/UserTest.php
index f15c854..603e566 100644
--- a/tests/AppBundle/Validator/UserTest.php
+++ b/tests/AppBundle/Validator/UserTest.php
## -44,10 +44,7 ## class UserTest extends WebTestCase{
$this->container = $this->client->getContainer();
$this->entityManager = $this->container->get('doctrine.orm.entity_manager');
- // Set validator
- $kernel = $this->createKernel();
- $kernel->boot();
- $this->validator = $kernel->getContainer()->get('validator');
+ $this->validator = $this->client->getContainer()->get('validator');
// Create one user
$this->createOneUser();
## -100,7 +97,7 ## class UserTest extends WebTestCase{
// you may need to use a different token class depending on your application.
// for example, when using Guard authentication you must instantiate PostAuthenticationGuardToken
$token = new PostAuthenticationGuardToken($user, $firewallName, [new Role('ROLE_CLIENT')]);
- self::$kernel->getContainer()->get('security.token_storage')->setToken($token);
+ $this->client->getContainer()->get('security.token_storage')->setToken($token);
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
I want to test my controller's functions with PHP unit using MOCK (in an API symfony 4) but i can't find a way to.
In fact i have do several research and coudn't find a way to do it, I installed PHP unit and coded a test web case without mock and it's work it but not by mock. i want to use mocking because i don't want to touch the database.
Anyone can help me please to achieve that .
This is my controller:
<?php
namespace App\Controller;
use App\Utils\MatchServiceInterface;
use App\Utils\ModeServiceInterface;
use App\Utils\PlayerServiceInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/api/scouters/matchs")
* Manage entity Match
* Class MatchController
* #package App\Controller
*/
class MatchController extends AbstractController
{
private $playerService;
private $modeService;
private $matchService;
/**
* ScouterController constructor.
* #param PlayerServiceInterface $playerService
* #param ModeServiceInterface $modeService
* #param MatchServiceInterface $matchService
*/
public function __construct(PlayerServiceInterface $playerService, ModeServiceInterface $modeService, MatchServiceInterface $matchService)
{
$this->playerService = $playerService;
$this->modeService = $modeService;
$this->matchService = $matchService;
}
/**
* #Route("/create", name="create_match", methods={"POST"})
* #param Request $request
* #return JsonResponse
*/
public function createMatch(Request $request): JsonResponse
{
$content = json_decode($request->getContent(), true);
$message = $this->playerService->assignPlayerToMatch($content);
return new JsonResponse($message, JsonResponse::HTTP_OK);
}
/**
* #Route("/mode", name="mode_match", methods={"POST"})
* #param Request $request
* #return JsonResponse
* #return JsonResponse
*/
public function modeMatch(Request $request): JsonResponse
{
$content = json_decode($request->getContent(), true);
$message = $this->modeService->assignModeToMatch($content);
return new JsonResponse($message, JsonResponse::HTTP_OK);
}
/**
* #Route("/features", name="feat_mode_match", methods={"POST"})
* #param Request $request
* #return JsonResponse
*/
public function featMode(Request $request): JsonResponse
{
$content = json_decode($request->getContent(), true);
$this->matchService->modeFeature($content);
$message = $this->matchService->modeFeature($content);
return new JsonResponse($message, JsonResponse::HTTP_OK);
}
}
this is an example of test of my login function ,
/**
* test ScouterController:login
*/
public function testLogin()
{
$data = ['email' => 'meher.jaber#solixy.com', 'password' => 'FootStat#2019'];
$client = static::createClient();
$request = $client->request('POST', '/api/scouter/login', [], [], [], json_encode($data));
$response = $client->getResponse();
$this->assertSame('"EmailPasswordValidAndHasNotPlayer"', $response->getContent());
$this->assertSame(200, $response->getStatusCode());
}
Login function :
/**
* Login Scouter
* #Route("/login", name="login_scouter", methods={"POST"})
* #param Request $request
* #return JsonResponse
*/
public function login(Request $request): JsonResponse
{
$content = json_decode($request->getContent(), true);
$message = $this->securityService->loginScouter($content);
$this->securityService->loginScouter($content);
return new JsonResponse($message, JsonResponse::HTTP_OK);
}
Login service :
/**
* #param $content
* #return array|string
*/
public function loginScouter($content)
{
$scouter = $this->em->getRepository(Scouter::class)->findOneBy(['email' => $content['email']]);
if ($scouter) {
/* #var $scouter Scouter */
if ($this->encoder->isPasswordValid($scouter, $content['password'])) {
if (is_null($scouter->getPlayer())) {
$message = "EmailPasswordValidHasNotPlayer";
} else {
/* #var $player Player */
$player = $scouter->getPlayer();
/* #var $lastMatch Match */
$lastMatch = $this->em->getRepository(Match::class)->findLastInserted($player->getId());
$message = ['idPlayer' => $player->getId(), 'firstName' => $player->getFirstName(), 'lastName' => $player->getFamilyName(), 'team' => $lastMatch->getTeam(), 'oppTeam' => $lastMatch->getOppositeTeam()];
}
} else {
$message = "PasswordInvalid";
}
} else {
$message = "EmailInvalid";
}
return $message;
}
I am trying to store cookies but it's not working. What seems to be the problem?
Service Class:
<?php
namespace AppBundle\Service;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
class MyUser
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack->getCurrentRequest();
}
// Adds a location to the user's wishlist
public function addToWishlist($locationId)
{
$cookies = $this->requestStack->cookies;
error_log(print_r($cookies, true));
$wishlist = $cookies->get('myWishlist', array());
if (!in_array($locationId, $wishlist))
{
$wishlist[] = $locationId;
}
$wishlistCookie = new Cookie('myWishlist', $wishlist, strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie);
}
/**
* Returns an array of the user's wishlist IDs
*
* #return array
*/
public function getWishlistIDs()
{
$cookies = $this->requestStack->cookies;
return $cookies->get('myWishlist', array());
}
/**
* Returns a count of the number of items in the wishlist
*
* #return int
*/
public function getWishlistCount()
{
return count($this->getWishlistIDs());
}
/**
* Checks if a location is in this user's wishlist.
*
* #param $id A location ID
* #return boolean
*/
public function locationInWishlist($id)
{
$cookies = $this->requestStack->cookies;
$wishlist = $cookies->get('myWishlist', array());
return (in_array($id, $wishlist));
}
/**
* Removes a location from the user's wishlist,
* by location ID
*
* #param $id
* #return void
*/
public function removeFromWishlistById($id)
{
$cookies = $this->requestStack->cookies;
$id = abs(intval($id));
$wishlist = $cookies->get('myWishlist', array());
if (in_array($id, $wishlist))
{
$vals = array_flip($wishlist);
unset($vals[$id]);
$wishlist = array_flip($vals);
}
$wishlistCookie = new Cookie('myWishlist', $wishlist, strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie); }
/**
* Removes all venues from the user's wishlist
*
* #return void
*/
public function removeAllFromWishlist()
{
$wishlistCookie = new Cookie('myWishlist', array(), strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie);
}
}
Controller Class:
<?php
namespace AppBundle\Controller\Frontend;
use AppBundle\Entity\LocationStat;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use AppBundle\Entity\Enquiry;
use AppBundle\Entity\EventType;
use AppBundle\Entity\PcawCodes;
use AppBundle\Entity\Setting;
use AppBundle\Entity\Thumbnail;
use AppBundle\Entity\WishlistCount;
use AppBundle\Form\Frontend\EnquiryForm;
use AppBundle\Service\MyUser;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Entity\Location;
use AppBundle\Entity\Wishlist;
use AppBundle\Service\UniqueValues;
use AppBundle\Form\Frontend\WishlistSaveEmailForm;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class WishlistsController extends Controller
{
/**
* #Route("wishlists/addVenueAjax/id/{id}", name="add_venue_ajax")
*/
public function addVenueAjaxAction(Request $request)
{
$doctrine = $this->getDoctrine();
if (!$request->isXmlHttpRequest())
return $this->redirectToRoute('homepage');
$id = $request->get("id", false);
$id = abs(intval($id));
if (!$id)
return new JsonResponse(array("error" => true, "render" => ""));
// Look up location. We can only add it if it's published etc.
$loc = $doctrine->getRepository(Location::class)->find($id);
if (!$loc || !$loc->getPublishedStatus())
return new JsonResponse(array("error" => true, "render" => ""));
// Add location to wishlist
$user = $this->get(MyUser::class);
$user->addToWishlist($id);
return new JsonResponse(array("error" => false, "newCount" => $user->getWishlistCount(), 'render' => $this->renderView('frontend/wishlists/partials/_itemwishlistaction.html.twig', array('locationid' => $loc->getId(),'user' => $user))));
}
}
But I am seeing no cookies in response section of the profiler:
Actually I got it working now.
1) I was not sending the headers after setting the cookie through $response->send()
2) There is an issue with setting array as cookie so instead I encoded the array into json & then decoded upon reading it again so that's how I stored array into cookie.
UPDATED CODE:
namespace AppBundle\Service;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
class MyUser
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack->getCurrentRequest();
}
// Adds a location to the user's wishlist
public function addToWishlist($locationId)
{
$cookies = $this->requestStack->cookies;
$wishlist = json_decode($cookies->get('myWishlist', "[]"));
if (!in_array($locationId, $wishlist))
{
$wishlist[] = $locationId;
}
$wishlistCookie = new Cookie('myWishlist', json_encode($wishlist), strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie);
$response->send();
}
/**
* Returns an array of the user's wishlist IDs
*
* #return array
*/
public function getWishlistIDs()
{
$cookies = $this->requestStack->cookies;
return json_decode($cookies->get('myWishlist', "[]"));
}
/**
* Returns a count of the number of items in the wishlist
*
* #return int
*/
public function getWishlistCount()
{
return count($this->getWishlistIDs());
}
/**
* Checks if a location is in this user's wishlist.
*
* #param $id A location ID
* #return boolean
*/
public function locationInWishlist($id)
{
$cookies = $this->requestStack->cookies;
$wishlist = json_decode($cookies->get('myWishlist', "[]"));
return (in_array($id, $wishlist));
}
/**
* Removes a location from the user's wishlist,
* by location ID
*
* #param $id
* #return void
*/
public function removeFromWishlistById($id)
{
$cookies = $this->requestStack->cookies;
$id = abs(intval($id));
$wishlist = json_decode($cookies->get('myWishlist', "[]"));
if (in_array($id, $wishlist))
{
$vals = array_flip($wishlist);
unset($vals[$id]);
$wishlist = array_flip($vals);
}
$wishlistCookie = new Cookie('myWishlist', json_encode($wishlist), strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie);
$response->send();
}
/**
* Removes all venues from the user's wishlist
*
* #return void
*/
public function removeAllFromWishlist()
{
$wishlistCookie = new Cookie('myWishlist', "[]", strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($wishlistCookie);
$response->send();
}
}
?>
Use these line above of class
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;
Set cookie and send to client using Response
$cookie = new Cookie('my_cookie', 'abcd', strtotime("+1 year"));
$response = new Response();
$response->headers->setCookie($cookie);
$response->sendHeaders();
Get cookie
$cookies = $request->cookies;
$test = $cookies->get('my_cookie');
dump($test);
I am developing a Symfony app with a REST API integrated but I'm facing a problem, when returning an user entity as JSON through an API request it returns the user password and despite being encrypted I would like to avoid it.
My user entity is:
<?php
namespace AppBundle\Entity;
use AppBundle\Util\Language;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
public function __construct()
{
$this->isActive = true;
}
// Functions and parameters
/**
* Set password
*
* #param string $password
*
* #return User
*/
public
function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
// More functions and parameters
/** #see \Serializable::serialize() */
public
function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
$this->isActive,
$this->createdAt,
$this->lastLogin,
));
}
/** #see \Serializable::unserialize() */
public
function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
$this->isActive,
$this->createdAt,
$this->lastLogin,
) = unserialize($serialized);
}
}
The User repository
<?php
namespace AppBundle\Repository;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository implements UserLoaderInterface
{
public function loadUserByUsername($username)
{
return $this->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
}
I have an static method to build API responses
public static function createSuccessfulresponse($entity, $entityName, $responseCode, $userLocale = "en", $responseMsg = "")
{
$defResponseMsg = ($responseMsg != "" ? $responseMsg : ApiResponseCode::getMsg($responseCode, $userLocale));
$responseArray = array();
$responseArray['responseCode'] = $responseCode;
$responseArray['responseMsg'] = $defResponseMsg;
$responseArray['userLocale'] = $userLocale;
if ($entity != null) {
$responseArray[$entityName] = $entity;
}
return ApiResponseHelper::serializeResponse($responseArray);
}
The response serializer
private static function serializeResponse($responseArray)
{
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
return $serializer->serialize($responseArray, 'json');
}
And one of the API calls which returns an user entity (there are more)
/**
* #Route("/api/url/{uid}" )
* #Method({"GET"})
*/
public function getByUidAction($uid)
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$entityManager = $this->getDoctrine()->getManager();
$entity = $entityManager->getRepository('AppBundle:Workday')->findOneBy(['uid' => $uid, 'user' => $user]);
if($entity != null){
return new Response(ApiResponseHelper::createSuccessfulresponse($entity, "workday", ApiResponseCode::SUCCESS_FETCH_WORKDAY, $user->getLocale()));
}else{
return new Response(ApiResponseHelper::createSuccessfulresponse(null, "workday", ApiResponseCode::ERROR_EXISTS_WORKDAY, $user->getLocale()));
}
}
This is one JSON response from the above method
{
"responseCode": "successfulResponseCode",
"responseMsg": "Data received",
"userLocale": "es",
"workday": {
"id": 10,
... so many data
"job": {
"id": 11,
.. more json data
},
"user": {
"username": "amendez",
"password": "encrypted_password",
... more data
},
... and more data
}
}
As you can see I receive a JSON object with the user which contains its encrypted password and many other data, my goal is to avoid returning the password key and value.
Does somebody know how could I achieve it?
You would need to define the Serializer groups with the desired getters assigned. See: https://symfony.com/doc/current/components/serializer.html#attributes-groups.
The preferred (best-practice) method would be to assign the desired groups.
use Symfony\Component\Serializer\Annotation\Groups;
class User implements AdvancedUserInterface, \Serializable
{
/**
* #Groups({"api"})
* #return string
*/
public function getUsername()
{
return $this->username;
}
//...
/**
* Get password
* #return string
*/
public function getPassword()
{
return $this->password;
}
}
Edited for easier use of Serializer service
In your app/config/config.yml enable annotations, which in-turn enables the serializer service.
#config.yml
framework:
#...
serializer:
enable_annotations: true
Now you can call the Serializer service directly or use DI in your custom services.
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\HttpFoundation\JsonResponse;
private static function serializeResponse($responseArray)
{
$serializer = $this->container->get('serializer');
return $serializer->serialize($responseArray, JsonEncoder::FORMAT, array(
'groups' => array('api'),
'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS
));
}
To manually use the Serializer Component with groups.
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
private static function serializeResponse($responseArray)
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
return $serializer->serialize($responseArray, JsonEncoder::FORMAT, array('groups' => array('api')));
}
Alternatively you should be able to set it as part of the ignored attributes. See: https://symfony.com/doc/current/components/serializer.html#ignoring-attributes
private static function serializeResponse($responseArray)
{
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('password'));
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
return $serializer->serialize($responseArray, JsonEncoder::FORMAT);
}
I want to override FOSUserBundle so that I can add extra fields(name,avatar,...) to user entity .
I also want to create a command like fos:user:create for creating user,so I create createUserCommand.php and override UserManipulator.php but when runnig command it comes with this error Column 'name' cannot be null
I think I must override UserInteface,UserManager and ...
But in this way I have to override almost whole FOSUserBundle !!
Is there any good tutorial that explain how to do this job ?
Symp/UserBundle/Util/UsertManipulator
<?php
namespace Symp\UserBundle\Util;
use FOS\UserBundle\Model\UserManagerInterface;
class UserManipulator
{
/**
* User manager
*
* #var UserManagerInterface
*/
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
/**
* Creates a user and returns it.
*
* #param string $username
* #param string $password
* #param string $email
* #param Boolean $active
* #param Boolean $superadmin
*
* #return \FOS\UserBundle\Model\UserInterface
*/
public function create($username, $password, $email, $active, $superadmin,$name)
{
$user = $this->userManager->createUser();
$user->setName($name);
$user->setUsername($username);
$user->setEmail($email);
$user->setPlainPassword($password);
$user->setEnabled((Boolean) $active);
$user->setSuperAdmin((Boolean) $superadmin);
$this->userManager->updateUser($user);
return $user;
}
/**
* Activates the given user.
*
* #param string $username
*/
public function activate($username)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
$user->setEnabled(true);
$this->userManager->updateUser($user);
}
/**
* Deactivates the given user.
*
* #param string $username
*/
public function deactivate($username)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
$user->setEnabled(false);
$this->userManager->updateUser($user);
}
/**
* Changes the password for the given user.
*
* #param string $username
* #param string $password
*/
public function changePassword($username, $password)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
$user->setPlainPassword($password);
$this->userManager->updateUser($user);
}
/**
* Promotes the given user.
*
* #param string $username
*/
public function promote($username)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
$user->setSuperAdmin(true);
$this->userManager->updateUser($user);
}
/**
* Demotes the given user.
*
* #param string $username
*/
public function demote($username)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
$user->setSuperAdmin(false);
$this->userManager->updateUser($user);
}
/**
* Adds role to the given user.
*
* #param string $username
* #param string $role
*
* #return Boolean true if role was added, false if user already had the role
*/
public function addRole($username, $role)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
if ($user->hasRole($role)) {
return false;
}
$user->addRole($role);
$this->userManager->updateUser($user);
return true;
}
/**
* Removes role from the given user.
*
* #param string $username
* #param string $role
*
* #return Boolean true if role was removed, false if user didn't have the role
*/
public function removeRole($username, $role)
{
$user = $this->userManager->findUserByUsername($username);
if (!$user) {
throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username));
}
if (!$user->hasRole($role)) {
return false;
}
$user->removeRole($role);
$this->userManager->updateUser($user);
return true;
}
}
Symp/UserBundle/Command/CreateUserCommand
<?php
namespace Symp\UserBundle\Command;
use FOS\UserBundle\Command\CreateUserCommand as BaseCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class CreateUserCommand extends BaseCommand
{
/**
* #see Command
*/
protected function configure()
{
$this
->setName('symp:user:create')
->setDescription('Create a user.')
->setDefinition(array(
new InputArgument('name', InputArgument::REQUIRED, 'The name of user'),
new InputArgument('username', InputArgument::REQUIRED, 'The username'),
new InputArgument('email', InputArgument::REQUIRED, 'The email'),
new InputArgument('password', InputArgument::REQUIRED, 'The password'),
new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Set the user as super admin'),
new InputOption('inactive', null, InputOption::VALUE_NONE, 'Set the user as inactive'),
))
->setHelp(<<<EOT
The <info>fos:user:create</info> command creates a user:
<info>php app/console fos:user:create matthieu</info>
This interactive shell will ask you for an email and then a password.
You can alternatively specify the email and password as the second and third arguments:
<info>php app/console fos:user:create matthieu matthieu#example.com mypassword</info>
You can create a super admin via the super-admin flag:
<info>php app/console fos:user:create admin --super-admin</info>
You can create an inactive user (will not be able to log in):
<info>php app/console fos:user:create thibault --inactive</info>
EOT
);
}
/**
* #see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$username = $input->getArgument('username');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
$inactive = $input->getOption('inactive');
$superadmin = $input->getOption('super-admin');
$manipulator = $this->getContainer()->get('symp_user.util.user_manipulator');
$manipulator->create($username, $password, $email, !$inactive, $superadmin,$name);
$output->writeln(sprintf('Created user <comment>%s</comment>', $username));
}
/**
* #see Command
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
if (!$input->getArgument('name')){
$name = $this->getHelper('dialog')->askAndValidate(
$output,
'Please choose a name: ',
function($name){
if(empty($name)){
throw new \Exception('Name can not be empty');
}
}
);
$input->setArgument('name',$name);
}
parent::interact($input,$output);
}
}
The error occurs because $name is never set. In the following $name is passed to the manipulator with a setter; $name does not appear in the argument list.
Instead try this:
services.yml
app.user_manipulator:
class: AppBundle\Tools\UserManipulator
arguments: [#fos_user.user_manager]
CreateUserCommand modification
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* #author Matthieu Bontemps <matthieu#knplabs.com>
* #author Thibault Duplessis <thibault.duplessis#gmail.com>
* #author Luis Cordova <cordoval#gmail.com>
*/
class CreateUserCommand extends ContainerAwareCommand
{
/**
* #see Command
*/
protected function configure()
{
$this
->setName('app:user:create')
->setDescription('Create a user.')
->setDefinition(array(
new InputArgument('username', InputArgument::REQUIRED, 'A username'),
new InputArgument('name', InputArgument::REQUIRED, 'A name'),
new InputArgument('email', InputArgument::REQUIRED, 'An email'),
new InputArgument('password', InputArgument::REQUIRED, 'A password'),
new InputOption('inactive', null, InputOption::VALUE_NONE, 'Set the user as inactive'),
new InputOption('superadmin', null, InputOption::VALUE_NONE, 'Set the user as superadmin'),
))
->setHelp(<<<EOT
The <info>app:user:create</info> command creates a user:
<info>php app/console app:user:create bborko</info>
This interactive shell will ask you for ...
EOT
);
}
/**
* #see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$username = $input->getArgument('username');
$name = $input->getArgument('name');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
$inactive = $input->getOption('inactive');
$superadmin = $input->getOption('superadmin');
$manipulator = $this->getContainer()->get('app.user_manipulator');
$manipulator->setName($name);
$manipulator->create($username, $password, $email, !$inactive, $superadmin);
$output->writeln(sprintf('Created user <comment>%s</comment>', $username));
}
/**
* #see Command
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$helper = $this->getHelper('question');
if (!$input->getArgument('username')) {
$question = new Question('Please enter a username: ');
$question->setValidator(function ($answer) {
if (empty($answer)) {
throw new \RuntimeException(
'A username is required'
);
}
return $answer;
});
$question->setMaxAttempts(2);
$input->setArgument('username', $helper->ask($input, $output, $question));
}
if (!$input->getArgument('name')) {
$question = new Question('Please enter a name: ');
$question->setValidator(function ($answer) {
if (empty($answer)) {
throw new \RuntimeException(
'A name is required'
);
}
return $answer;
});
$question->setMaxAttempts(2);
$input->setArgument('name', $helper->ask($input, $output, $question));
}
if (!$input->getArgument('email')) {
$question = new Question('Please enter an email: ');
$question->setValidator(function ($answer) {
if (empty($answer)) {
throw new \RuntimeException(
'An e-mail address is required'
);
}
return $answer;
});
$question->setMaxAttempts(2);
$input->setArgument('email', $helper->ask($input, $output, $question));
}
if (!$input->getArgument('password')) {
$question = new Question('Please enter a password: ');
$question->setValidator(function ($answer) {
if (empty($answer)) {
throw new \RuntimeException(
'A password is required'
);
}
return $answer;
});
$question->setMaxAttempts(5);
$input->setArgument('password', $helper->ask($input, $output, $question));
}
}
}
UserManipulator
namespace AppBundle\Tools;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\UserManipulator as Manipulator;
/**
* Executes some manipulations on the users
*
* #author Christophe Coevoet <stof#notk.org>
* #author Luis Cordova <cordoval#gmail.com>
*/
class UserManipulator extends Manipulator
{
/**
* User manager
*
* #var UserManagerInterface
*/
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
/**
* Creates a user and returns it.
*
* #param string $username
* #param string $password
* #param string $email
* #param Boolean $active
* #param Boolean $superadmin
*
* #return \FOS\UserBundle\Model\UserInterface
*/
public function create($username, $password, $email, $active, $superadmin)
{
$user = $this->userManager->createUser();
$user->setUsername($username);
$user->setName($this->name);
$user->setEmail($email);
$user->setPlainPassword($password);
$user->setEnabled((Boolean) $active);
$this->userManager->updateUser($user, true);
return $user;
}
public function setName($name)
{
$this->name = $name;
}
}