Symfony 4 API Rest PUT : Map data to database entity - api

I'm a begginer in Symfony 4 and I'm developing an API Rest. I want to create a PUT resource that can handle many update cases.
In my case, I have a user with many properties but I will take an example with 3 properties to keep things simple :
User {
username,
email,
password
}
My PUT resource can be called in order to update Username, update Email or update Password. For example, to update Username, the user of my API will send a PUT request with only username :
{
username: "New username"
}
Same for email and password, he will only send the property he wants to change.
My problem is in my Controller, I have to do things like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(Request $request)
{
$userId = $request->get('id');
$user = $this->doctrine->getRepository(User::class)->findOneBy('id' => $userId);
$userFromRequest = $this->serializer->deserialize($request->getContent(), User::class, 'json');
if ($userFromRequest->getUsername() != NULL) {
$user->setUsername($userFromRequest->getUsername())
}
if ($userFromRequest->getEmail() != NULL) {
$user->setEmail($userFromRequest->getEmail())
}
if ($userFromRequest->getPassword() != NULL) {
$user->setPassword($userFromRequest->getPassword())
}
// ...
}
In my example I have only 3 properties, but imagine when I have 30 properties.
With Symfony 3 I used forms to validate / save my datas :
$form->submit($request->request->all(), $clearMissing);
Where $clearMissing is false to keep datas not provided by the user of my API. I can't find a way to do it with serializer but I guess I'm doing things wrong.
Thanks.

If I understand correctly, You can use the validator Component like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(User $user, Request $request, ValidatorInterface $validator)
{
$data = $request->getContent();
$this->serializer->deserialize($data, User::class, 'json', ['object_to_populate' => $user]);
//At this moment, the data you sent is merged with your user entity
/** #var ConstraintViolationList $errors */
$errors = $validator->validate($user, null ,['groups' => 'user_update']);
if (count($errors) > 0) {
//return json reponse with formated errors;
}
//if ok $entityManager->flush() and Response Json with serialization group;
...
}
In your user class :
class User implements UserInterface
{
/**
* #Assert\Email(groups={"user_create", "user_update"})
*/
private $email;
/**
* #Assert\NotBlank(groups={"user_create", "user_update"})
* #Assert\Length(min=7, groups={"user_create", "user_update"})
*/
private $password;
/**
* #Assert\Length(min=2, groups={"user_create", "user_update"} )
*/
private $username;
}
Related Validator component documentation : https://symfony.com/doc/current/validation/groups.html
You can also check this project : https://github.com/attineos/relation-deserializer

Related

How to enable email verification link to expire after verification in laravel 6 API implemented using VerifiesEmail feature?

I have implemented Laravel 6 API and used Laravel's inbuilt Illuminate\Foundation\Auth\VerifiesEmails based on tutorial here but the email verification link is not expired and still accessible after successful email verification. I have found many tutorials regarding laravel frontend but how to implement it on API.
VerificationApiController
class VerificationApiController extends Controller
{
use VerifiesEmails;
/**
* Mark the authenticated user's email address as verified.
* #param Request $request
* #return JsonResponse
*/
public function verify(Request $request): JsonResponse
{
$userID = $request['id'];
$user = User::findOrFail($userID);
$date = date('Y-m-d g:i:s');
// to enable the “email_verified_at field of that
// user be a current time stamp by mimicking the
// must verify email feature
$user->email_verified_at = $date;
$user->save();
return response()->json('Email verified!');
}
/**
* Resend the email verification notification.
* #param Request $request
* #return JsonResponse|Response
*/
public function resend(Request $request)
{
if ($request->user()->hasVerifiedEmail()) {
return response()->json('User already have verified email!', 422);
}
$request->user()->sendEmailVerificationNotification();
return response()->json('The notification has been resubmitted');
// return back()->with(‘resent’, true);
}
}
User model
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, Notifiable;
protected $fillable = [
'name', 'email', 'password'
];
/**
* The attributes that should be hidden for arrays.
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* Send email verification notification
*/
public function sendApiEmailVerificationNotification()
{
$this->notify(new VerifyApiEmail); // my notification
}
}
Here are verification api routes
Route::get(‘email/verify/{id}’, ‘VerificationApiController#verify’)->name(‘verificationapi.verify’);
Route::get(‘email/resend’, ‘VerificationApiController#resend’)->name(‘verificationapi.resend’)
Here is UsersApiController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Hash;
use Auth;
use Validator;
use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Auth\Events\Verified;
class UsersApiController extends Controller
{
use VerifiesEmails;
public $successStatus = 200;
/**
* login api
*
* #return \Illuminate\Http\Response
*/
public function login(){
if(Auth::attempt([‘email’ => request(‘email’), ‘password’ => request(‘password’)])){
$user = Auth::user();
if($user->email_verified_at !== NULL){
$success[‘message’] = “Login successfull”;
return response()->json([‘success’ => $success], $this-> successStatus);
}else{
return response()->json([‘error’=>’Please Verify Email’], 401);
}
}
else{
return response()->json([‘error’=>’Unauthorised’], 401);
}
}
/**
* Register api
*
* #return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
‘name’ => ‘required’,
‘email’ => ‘required|email’,
‘password’ => ‘required’,
‘c_password’ => ‘required|same:password’,
]);
if ($validator->fails()) {
return response()->json([‘error’=>$validator->errors()], 401);
}
$input = $request->all();
$input[‘password’] = Hash::make($input[‘password’]);
$user = User::create($input);
$user->sendApiEmailVerificationNotification();
$success[‘message’] = ‘Please confirm yourself by clicking on verify user button sent to you on your email’;
return response()->json([‘success’=>$success], $this-> successStatus);
}
/**
* details api
*
* #return \Illuminate\Http\Response
*/
public function details()
{
$user = Auth::user();
return response()->json([‘success’ => $user], $this-> successStatus);
}
}
Here are user and auth routes
Route::post(‘login’, ‘UsersApiController#login’);
Route::post(‘register’, ‘UsersApiController#register’);
Route::group([‘middleware’ => ‘auth:api’], function(){
Route::post(‘details’, ‘UsersApiController#details’)->middleware(‘verified’);
}); // will work only when user has verified the email
so the problem is that when I click on verification link on email the user is verified but the link is not expired . I want the link to be expired as soon as user is verified. How to do that?
Have you implemented the VerifyApiEmail class?
namespace App\Notifications;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Auth\Notifications\VerifyEmail as VerifyEmailBase;
class VerifyApiEmail extends VerifyEmailBase
{
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'api.auth.verify', Carbon::now()->addMinutes(60), ['id' => $notifiable->getKey()]
);
}
}
Here you can add the expiration time in minutes, seconds or hours.

FOSUserBundle - changes in default configuration, overriding some default settings

I am quite new in Symfony 2. I have to build an application in Symfony 2 (I'm using 2.8 and also latest version of FOSUser Bundle). Majority of work is done, although I have to do some changes in default security settings. I was looking for them for two days and I have only really foggy concept how this can be fixed. The supposed changes are following:
REGISTRATION - I have enabled confirmation by e-mail and I don't know how to make to the confirmation link expire after one hour and/or after one usage (one click). There is setting in config.yaml settting ttl, but only for password resseting.
REGISTRATION - Before confirming users are prevented from logging in and there is Symfony 2 Exception - Disabled Account working, rendering short message about it. Actually I have to set redirection to another page (I suppose template) to render custom message that 'this account is blocked...' and link to send another link with confirmation token (Am I right? The link in email is confirmation token?).
RESETTING - As mentioned before ttl for link (token?) ressetting password is set for one hour, but I don't know how to make it expire after one usage (one click).
I know how to override some template of FOSUser, but I have no clear idea which of files should I override to change these things.
I have noticed that my FOSUser uses Symfony 2 Exceptions files and if I have changed content of message in my Exception file, it has changed also on my page, but I don't know how to make it well and override it, adding all necessary features.
I was trying to override AuthenticationListener (from FOSUser) with use of Compiler Pass (http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html), but I don't know if it worked, because any changes in overriden Listener were not visible. Actually I don't know if this is the file I should override.
I have check a few Stackoverflow questions, but I haven't found an answer.
FOSUser Bundle - Prevent Disabled users from logging in
This doesn't work for me, because users are prevented and I need only override message of exception and create redirection with another link sending confirmation one.
FOS user bundle authentication
I have tried to implement the solution pointed here, but it didn't work and I am not sure if I really need such a complicated solution.
Thanks for help in advance and I someone need to see my files, configuration just write and I will post here necessary ones.
I have finally figured all these things out. If someone has a similar problem i advise to read these topics in Symfony documentation:
Overriding FOSUser B controllers
Hooking into a controller
It turns out that for described features I need to override some files form FOSUser Bundle, sometimes Controller was enough, sometimes I needed to modify EventListener (actually I have even created my own event). There is more than one way.
The hardest part was one-click link for ressetting password. I have used a flag, which is set to false while sending an e-mail and set true while clicking on link to prevent from using link once more. The problem is, that Resetting Controller is 'executed' two times, so while clicking submit there was redirection, beacuse flag was true. I have added some counting in session in order to omit the part of code which checks the flag, when you hit the submit button (second usage of the Reset Method in Resetting Controller), but it prevented only from clicking submit second time, so actually you can not use the link two times, but you can see form two times, which is not an effect I wanted to reach, but is far better than nothing. If someone has an idea how to upgrade it I will be gratefull
<?php
namespace My\UserBundle\Controller;
use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;
/**
* Controller managing the resetting of the password
*
* #author Thibault Duplessis <thibault.duplessis#gmail.com>
* #author Christophe Coevoet <stof#notk.org>
*/
class ResettingController extends FOSResettingController
{
/**
* Request reset user password: submit form and send email
*/
public function sendEmailAction(Request $request)
{
$username = $request->request->get('username');
/** #var $user UserInterface */
$user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);
if (null === $user) {
return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
'invalid_username' => $username
));
}
if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
}
if (null === $user->getConfirmationToken()) {
/** #var $tokenGenerator \FOS\UserBundle\Util\TokenGeneratorInterface */
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
}
$this->get('fos_user.mailer')->sendResettingEmailMessage($user);
$user->setPasswordRequestedAt(new \DateTime());
$user->setPasswordRequestedClicked(false);
$this->get('fos_user.user_manager')->updateUser($user);
$_SESSION['views'] = 1;
return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
array('email' => $this->getObfuscatedEmail($user))
));
}
/**
* Reset user password
*/
public function resetAction(Request $request, $token)
{
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.resetting.form.factory');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->findUserByConfirmationToken($token);
//Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
if (null === $user) {
return $this->redirectToRoute('fos_user_invalid_token_click');
}
if ($_SESSION['views'] == 1){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
} else {
$user->setPasswordRequestedClicked(true);
$userManager->updateUser($user);
$_SESSION['views']++;
$_SESSION['views']++;
}
} else {
$_SESSION['views']++;
}
if ($_SESSION['views'] == 5){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
}
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$userManager->updateUser($user);
return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
public function InvalidTokenTtlMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
}
public function InvalidTokenClickMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
}
}
My listener:
<?php
namespace My\UserBundle\EventListener;
use FOS\UserBundle\EventListener\ResettingListener as FOSResettingListener;
use FOS\UserBundle\FOSUserEvents;
use My\UserBundle\UserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use My\UserBundle\Entity\User;
class ResettingListener extends FOSResettingListener
{
private $router;
private $tokenTtl;
public function __construct(UrlGeneratorInterface $router, $tokenTtl)
{
$this->router = $router;
$this->tokenTtl = $tokenTtl;
}
public static function getSubscribedEvents()
{
return array(
UserEvents::RESETTING_RESET_CLICK_CHECK => 'onResettingClickCheck',
FOSUserEvents::RESETTING_RESET_INITIALIZE => 'onResettingResetInitialize',
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onResettingResetSuccess',
);
}
public function onResettingClickCheck(GetResponseUserEvent $event){
//checking if link hasn't expired due to its usage
if ($event->getUser()->isPasswordRequestedClicked() === true){
$event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_click')));
}
}
public function onResettingResetInitialize(GetResponseUserEvent $event)
{
//checking if link hasn't expired due to exceeding token Ttl
if (!$event->getUser()->isPasswordRequestNonExpired($this->tokenTtl)) {
$event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_ttl')));
}
}
public function onResettingResetSuccess(FormEvent $event)
{
/** #var $user \FOS\UserBundle\Model\UserInterface */
$user = $event->getForm()->getData();
$user->setConfirmationToken(null);
$user->setPasswordRequestedAt(null);
$user->setEnabled(true);
}
}
and my User entity:
<?php
namespace My\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use My\BackendBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="My\UserBundle\Repository\UserRepository")
*/
class User extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable = false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Gedmo\Slug(fields={"username"})
* #ORM\Column(length=128, unique=true)
*/
private $slug;
/**
*
* #ORM\ManyToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="users")
* #ORM\JoinColumn(name="id", referencedColumnName="id", nullable=false)
* #ORM\OrderBy({"date"="ASC"})
*
*/
protected $events;
/**
* #var \Doctrine\Common\Collections\ArrayCollection $event_org
* #ORM\OneToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="user_org", cascade={"all"})
*/
protected $event_org;
/**
* #var \DateTime
* #ORM\Column(name="confirmation_token_requested_at", type="datetime")
*/
protected $confirmationTokenRequestedAt;
/**
* #var boolean
* #ORM\Column(name="password_requested_clicked", type="boolean", nullable=true)
*/
protected $passwordRequestedClicked;
public function __toString()
{
return $this->getUsername();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
public function __construct()
{
parent::__construct();
$this->event_org = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add event
*
* #param \My\BackendBundle\Entity\Event $event
*
* #return User
*/
public function addEvent(\My\BackendBundle\Entity\Event $event)
{
$this->events[] = $event;
$event->addUser($this);
return $this;
}
/**
* Remove event
*
* #param \My\BackendBundle\Entity\Event $event
*/
public function removeEvent(\My\BackendBundle\Entity\Event $event)
{
$this->events->removeElement($event);
}
/**
* Get events
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEvents()
{
return $this->events;
}
/**
* Set slug
*
* #param string $slug
*
* #return User
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Add eventOrg
*
* #param \My\BackendBundle\Entity\Event $eventOrg
*
* #return User
*/
public function addEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
{
$this->event_org[] = $eventOrg;
return $this;
}
/**
* Remove eventOrg
*
* #param \My\BackendBundle\Entity\Event $eventOrg
*/
public function removeEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
{
$this->event_org->removeElement($eventOrg);
}
/**
* Get eventOrg
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEventOrg()
{
return $this->event_org;
}
/**
* Set confirmationTokenRequestedAt
*
* #param \DateTime $confirmationTokenRequestedAt
*
* #return User
*/
public function setConfirmationTokenRequestedAt(\DateTime $date = null)
{
$this->confirmationTokenRequestedAt = $date;
return $this;
}
/**
* Gets the timestamp that the user requested a confirmation_token.
*
* #return null|\DateTime
*/
public function getConfirmationTokenRequestedAt()
{
return $this->confirmationTokenRequestedAt;
}
public function isConfirmationTokenNonExpired($ttl)
{
return $this->getConfirmationTokenRequestedAt() instanceof \DateTime &&
$this->getConfirmationTokenRequestedAt()->getTimestamp() + $ttl > time();
}
/**
* Set passwordRequestedClicked
*
* #param boolean $passwordRequestedClicked
*
* #return User
*/
public function setPasswordRequestedClicked($boolean)
{
$this->passwordRequestedClicked = (Boolean) $boolean;
return $this;
}
/**
* Get passwordRequestedClicked
*
* #return boolean
*/
public function getPasswordRequestedClicked()
{
return $this->passwordRequestedClicked;
}
/**
* Checks whether the user has used password request.
*
*
* #return Boolean true if the user is enabled, false otherwise
*/
public function isPasswordRequestedClicked() {
return $this->passwordRequestedClicked;
}
}
If someone would like to get code for remaining problems, please write me a message and I will provide it here :).
Resetting controller:
<?php
namespace My\UserBundle\Controller;
use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;
class ResettingController extends FOSResettingController
{
public function sendEmailAction(Request $request)
{
$username = $request->request->get('username');
$user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);
if (null === $user) {
return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
'invalid_username' => $username
));
}
if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
}
if (null === $user->getConfirmationToken()) {
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
}
$this->get('fos_user.mailer')->sendResettingEmailMessage($user);
$user->setPasswordRequestedAt(new \DateTime());
$user->setPasswordRequestedClicked(false);
$this->get('fos_user.user_manager')->updateUser($user);
$_SESSION['views'] = 1;
return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
array('email' => $this->getObfuscatedEmail($user))
));
}
public function resetAction(Request $request, $token)
{
$formFactory = $this->get('fos_user.resetting.form.factory');
$userManager = $this->get('fos_user.user_manager');
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->findUserByConfirmationToken($token);
//Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
if (null === $user) {
return $this->redirectToRoute('fos_user_invalid_token_click');
}
if ($_SESSION['views'] == 1){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
} else {
$user->setPasswordRequestedClicked(true);
$userManager->updateUser($user);
$_SESSION['views']++;
$_SESSION['views']++;
}
} else {
$_SESSION['views']++;
}
if ($_SESSION['views'] == 5){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
}
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$userManager->updateUser($user);
return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
public function InvalidTokenTtlMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
}
public function InvalidTokenClickMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
}
}

How do I create a RESTful API in Laravel to use in my BackboneJS app

I want to create a RESTful API in Laravel 4 to use in my BackboneJS apps. What is the best way for doing this? Does the Laravel 4 framework provides a good solution for this.
This is an example for creating an API that stores bookmarks. It uses the Route::resource() method.
Creating a RESTful controller in Laravel 4
POST = store() (Create a new entry)
DELETE = destroy($id) (Delete an entry)
GET = index() (Get all entries)
GET = show($id) (Get one entry)
PUT = update($id) (Update an entry)
The best extension for testing your API's:
Chrome extension Postman REST client
This is my simple router and controller, I did the same kind of project. You might want to try Postman RESTful client for Chrome to test your API,
routes.php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/
// Route group for API versioning
Route::group(array('prefix' => 'api/v1'), function() {
Route::resource('bookmarks', 'BookmarkController',
array('except' => array('create', 'edit')));
});
BookmarkController.php
class BookmarkController extends Controller {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index() {
return Bookmark::all();
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store() {
$bookmark = new Bookmark;
$bookmark->url = Input::get('url');
$bookmark->description = Input::get('description');
$bookmark->tags = Input::get('tags');
$bookmark->save();
return $bookmark;
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id) {
return Bookmark::find($id);
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id) {
$bookmark = Bookmark::find($id);
$bookmark->url = Input::get('url');
$bookmark->description = Input::get('description');
$bookmark->tags = Input::get('tags');
$bookmark->save();
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id) {
$bookmark = Bookmark::find($id)->delete();
}
}

Extra Attribute Disappears after it is set successfully

I am trying to get all records that are tied to a parent object through a lookup table, and insert them directly into the model. I have an object, Role, that hasMany() RoleEndpoints. RoleEndpoints belongs to Role and hasMany() Endpoints. All the data is being retrieved exactly as I expect, however, it seems to disappear after I set it.
<?php
class ACL {
private $_di;
public function __construct($di) {
$this->_di = $di;
}
public function createACL() {
if(!$this->_acl) {
$this->_acl = new stdClass();
$roles = $possibleRoles = Roles::find();
/**
* Check if there is at least one role out there
*/
if($roles->count() > 0) {
/**
* Iterate over all of the records
*/
while($roles->valid()) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach($roles->current()->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
I tried several different approaches; this seemed the best
*/
$roles->current()->endpoints = $endpoints;
}
/**
* Set object to loop through from the beginning
*/
$roles->rewind();
/**
* Here is where my issue lies.
*
* The endpoints attribute, which is set as a public attribute in the model class
* gets unset for some reason
*/
while($roles->valid()) {
echo '<pre>';
var_dump($roles->current());
exit;
}
As the comments say, during the second iteration of the result set, the endpoints attribute drops becomes null for some reason. Am I doing something wrong here? Am I missing a step?
Any help would be appreciated. Thank you!
There is a missing next() in the iterator traversing:
while ($roles->valid()) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach ($roles->current()->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
* I tried several different approaches; this seemed the best
*/
$roles->current()->endpoints = $endpoints;
//Missing next
$roles->next();
}
Also, you don't need to iterate the cursor in that way, just a foreach is easy to read and maintain:
$roles = Roles::find();
$roleEndpoints = array();
if (count($roles)) {
foreach ($roles as $role) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach ($role->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
* I tried several different approaches; this seemed the best
*/
$roleEndpoints[$role->id] = $endpoints;
}
}
//Get the endpoints
foreach ($roleEndpoints as $roleId => $endpoint) {
//...
}
Also, If this is a common task you can add a method to your model to reuse that logic:
class Roles extends Phalcon\Mvc\Model
{
public function getEndpoints()
{
$endpoints = array();
foreach ($this->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
return $endpoints;
}
public function initialize()
{
//...
}
}
So you can get your endpoints:
$roles = Roles::find();
if (count($roles)) {
foreach ($roles as $role) {
$endpoints = $role->getEndpoints();
}
}

UserIdentity and session issue

I have the following class. But when I try to access the Yii::app()->user->realName; it generates an error.
I can't understand it all. please help!
Following code is the code of my UserIdentity class.
<?php
/**
* UserIdentity represents the data needed to identity a user.
* It contains the authentication method that checks if the provided
* data can identity the user.
*/
class UserIdentity extends CUserIdentity {
public $id, $dmail, $real_name;
/**
* Authenticates a user.
* The example implementation makes sure if the username and password
* are both 'demo'.
* In practical applications, this should be changed to authenticate
* against some persistent user identity storage (e.g. database).
* #return boolean whether authentication succeeds.
*/
public function authenticate() {
$theUser = User::model()->findByAttributes(array(
'email' => $this->username,
// 'password' => $this->password
));
if ($theUser == null) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->id = $theUser->id;
$this->setState('uid', $this->id);
// echo $users->name; exit;
// $this->setState('userName', $theUser->name);
$this->setState("realName",$theUser->fname .' '. $theUser->lname);
$this->errorCode = self::ERROR_NONE;
}
return!$this->errorCode;
}
}
?>
You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_realName = 'wu_default';
public function getRealName(){
$realName = Yii::app()->user->getState('realName');
return (null!==$realName)?$realName:$this->_realName;
}
public function setRealName($value){
Yii::app()->user->setState('realName', $value);
}
}
You can then assign and recall the realName attribute by using Yii::app()->user->realName.
The protected $_realName is optional, but allows you to define a default value. If you choose not to use it, change the return line of the getRealName method to return $realName.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),