How to singularize route path part using FOSRestBundle - rest

I have this controller (a snippet here)
/**
* Class UserController
* #package Belka\AuthBundle\Controller
*/
class UserController extends FOSRestController implements ClassResourceInterface
{
/**
* #View()
*
* #Route(requirements={"user"="\w+"})
* #ParamConverter("user", converter="fos_rest.request_body")
*/
public function postGlobaltokenAction(User $user)
{
...
}
that automatically generates the route:
post_user_globaltoken POST ANY ANY /api/users/{user}/globaltokens.{_format}
which is OK, except for the fact I would like "globaltoken" singularized. Is that possible? I cannot find any annotation to tweak this. Should I hardcode the route in my route.yml?

I've found two ways:
Using a personal Inflector
as Symfony's documentation suggests, you can register a personal Inflector which returns "globaltoken" always as singular, whereas all the other resources will be pluralized:
use Doctrine\Common\Util\Inflector;
use FOS\RestBundle\Util\Inflector\DoctrineInflector;
use FOS\RestBundle\Util\Inflector\InflectorInterface;
/**
* Inflector class
*
*/
class NoopInflector extends DoctrineInflector implements InflectorInterface
{
public function pluralize($word)
{
if($word == "globaltoken")
return $word;
return parent::pluralize($word);
}
}
services.yml:
services:
belka.auth_bundle.util.inflector:
class: Belka\AuthBundle\Util\NoopInflector
but I found this way a bit dirty, as I could need the plural form in the future.
Overriding the FOSRestBundle auto-generated routes
It's that simple! Just add the #Route annotation on the right methos and you're done!
/**
* #View()
*
* #Route("/users/{user}/globaltoken", defaults={"_format" = "json"}, requirements={"user"="\w+"})
*
* #ParamConverter("user", converter="fos_rest.request_body")
*/
public function postAction(User $user)
{
}
Now if I call php app/console debug:route I get what I want:
post_user_globaltoken POST ANY ANY /api/users/{user}/globaltoken.{_format}

Related

Laravel Backpack basic request validation

I'm having an issue that I thought would be VERY simple to accomplish. I can not get this very basic request validation to work. I can enter "Bob" on the create form and the edit form and get no error messages. It simply inserts into the database.
Here's my code. I feel I'm doing/not doing something stupid.
UserCrudController.php
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\UserRequest;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
/**
* Class UserCrudController
* #package App\Http\Controllers\Admin
* #property-read \Backpack\CRUD\app\Library\CrudPanel\CrudPanel $crud
*/
class UserCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
/**
* Configure the CrudPanel object. Apply settings to all operations.
*
* #return void
*/
public function setup()
{
CRUD::setModel(\App\Models\User::class);
CRUD::setRoute(config('backpack.base.route_prefix') . '/user');
CRUD::setEntityNameStrings('user', 'users');
}
/**
* Define what happens when the List operation is loaded.
*
* #see https://backpackforlaravel.com/docs/crud-operation-list-entries
* #return void
*/
protected function setupListOperation()
{
CRUD::column('name');
CRUD::column('email');
//CRUD::column('password');
/**
* Columns can be defined using the fluent syntax or array syntax:
* - CRUD::column('price')->type('number');
* - CRUD::addColumn(['name' => 'price', 'type' => 'number']);
*/
}
/**
* Define what happens when the Create operation is loaded.
*
* #see https://backpackforlaravel.com/docs/crud-operation-create
* #return void
*/
protected function setupCreateOperation()
{
CRUD::setValidation(UserRequest::class);
CRUD::field('name');
CRUD::field('email');
//CRUD::field('password');
/**
* Fields can be defined using the fluent syntax or array syntax:
* - CRUD::field('price')->type('number');
* - CRUD::addField(['name' => 'price', 'type' => 'number']));
*/
}
/**
* Define what happens when the Update operation is loaded.
*
* #see https://backpackforlaravel.com/docs/crud-operation-update
* #return void
*/
protected function setupUpdateOperation()
{
$this->setupCreateOperation();
}
}
UserRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// only allow updates if the user is logged in
return backpack_auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => ['required','min:5','max:255'],
];
}
/**
* Get the validation attributes that apply to the request.
*
* #return array
*/
public function attributes()
{
return [
//
];
}
/**
* Get the validation messages that apply to the request.
*
* #return array
*/
public function messages()
{
return [
//
];
}
}
I think what is happening is that you are not loading that controller.
If you have installed PermissionManager and now want to configure the UserCrudController you need to either manually register the routes yourself and point to your new controller, or alternativelly (and probably recommended) bind your new controller to the package one so your controller gets "served" instead of the package controller.
// In AppServiceProvider.php or any other provider of your choice:
$this->app->bind(
\Backpack\PermissionManager\app\Http\Controllers\UserCrudController::class,
\App\Http\Controllers\Admin\UserCrudController::class
);
If you don't need to change everything in the Controller you can directly extend the package UserCrudController and only change the things you need.
<?php
namespace App\Http\Controllers\Admin;
class UserCrudController extends \Backpack\PermissionManager\app\Http\Controllers\UserCrudController
{
}
The package controller already extends the CrudController.
The validation is added on setupCreateOperation() that you can override to fit your needs.
Cheers

TYPO3 Extbase - Failing to render json via typnum

TYPO3 Extbase - Failing to render json via typnum
Next to list/edit/new/remove action (which work) I tried to render the output in json. But no values render. If I do a simple ...
$data = array('value'=>'001');
return json_encode($data);
It does return ...
{"value":"001"}
What am I missing?
Edit: With using and referencing to the same repository its working:
JSONController.php
<?php
namespace Vendor\Lei\Controller;
use Vendor\Lei\Domain\Model\Lei;
/**
* JSONController
*/
class JSONController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* leiRepository
*
* #var \Vendor\Lei\Domain\Repository\LeiRepository
* #inject
*/
protected $leiRepository;
/**
* #var string
*/
protected $defaultViewObjectName = 'TYPO3\CMS\Extbase\Mvc\View\JsonView';
/**
* action jsonRequest
*
* #return void
*/
public function jsonRequestAction() {
//$data = array('value'=>'001');
//return json_encode($data);
$this->view->setVariablesToRender(array('records'));
$this->view->assign('records', $this->leiRepository->jsonRequest());
}
}
LeiRepository.php
<?php
namespace Vendor\Lei\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
class LeiRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
...
public function jsonRequest() {
$query = $this->createQuery();
$result = $query->setLimit(100)->execute();
return $result;
}
}
If you inject and use a JsonRepository extbase expexts a domain object called Json. If you just want to render already existing domain objects as their JSON representation, just use the same repositories you used in your listAction() and detailAction().
Have a look at my example: https://usetypo3.com/json-view.html
Also, a debug after the return like you did in your repository will never be executed.

Typo3 Fluid extbase- How to execute external controller?

I have 2 different extension. I want to execute second controller (external) inside my first controller
Two different extension 1. Course , 2. Search
class CourseController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* courseRepository
*
* #var \TYPO3\Courses\Domain\Repository\CourseRepository
* #inject
*/
protected $courseRepository = NULL;
/**
* action list
*
* #return void
*/
public function listAction() {
/** I want to access Search extension Controller (f.e searchRepository->listAction() )**/
}
}
class SearchRepository extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* searchRepository
*
* #var \TYPO3\Courses\Domain\Repository\SearchRepository
* #inject
*/
protected $searchRepository = NULL;
/**
* action list
*
* #return void
*/
public function listAction() {
$searches = $this->searchRepository->findAll();
$this->view->assign('searches', $searches);
}
}
tl;dr:
Within a Controller, you usually forward() or redirect() to delegate to a different ControllerAction, e.g. delegate to SearchController::listAction() of 'myExtensionKey':
$this->forward('list', 'Search', 'myExtensionKey');
or
$this->redirect('list', 'Search', 'myExtensionKey');
Long version:
Quote from the MVC documentation of Flow which is quite similar to Extbase MVC:
Often, controllers need to defer execution to other controllers or
actions. For that to happen, TYPO3 Flow supports both, internal and
external redirects:
in an internal redirect which is triggered by forward(), the URI does
not change.
in an external redirect, the browser receives a HTTP
Location header, redirecting him to the new controller. Thus, the URI
changes.
The APIs are:
public void forward(string $actionName, string $controllerName=NULL, string $extensionName=NULL, array $arguments=NULL)
protected void redirect(string $actionName, string $controllerName=NULL, string $extensionName=NULL, array $arguments=NULL, integer $pageUid=NULL, int $delay=0, int $statusCode=303)
The programming API details can be found in the Extbase API

Extend function with Doctrine ORM Annotation

I've got the following situation: a (Doctrine Entity) ContentCategory that is extending the DataObject class. The DataObject class has the following function, onPrePersist:
/**
* #ORM\HasLifecycleCallbacks
*/
class DataObject implements InputFilterAwareInterface
{
...
/** #ORM\PrePersist */
public function onPrePersist()
{
//using Doctrine DateTime here
$this->creation_date = new \DateTime('now');
}
The ContentCategory class needs this function aswell. When I put this function in the ContentCategory class it works just fine. Is there a way whereby, the ContentCategory class can use the same function, onPrePersist() without defining it in the class itsself?
* #ORM\HasLifecycleCallbacks()
*/
class ContentCategory extends DataObject implements InputFilterAwareInterface
{
...
}
The reason to give objects the onPrePersist function, is to set a DateTime when this object is created or any other object / entity that is extending the DataObject class.
--< Edited >--
I've currently added a construct method to the ContentCategory like this:
public function __construct() {
parent::onPrePersist();
}
In this way Doctrine executes the function onPersist when a new Entity is created. The other case is when an enttiy is being updated, with Doctrine. I'll like to set a Modified_date. In that case there will be a function like this, in the DataObject class.
/**
* #ORM\HasLifecycleCallbacks
*/
class DataObject implements InputFilterAwareInterface
{
...
/**
* #ORM\PreUpdate
*/
public function onUpdate()
{
$this->last_modified_date = new \DateTime('now');
}
The Doctrine ORM Annotation (PreUpdate) that have been added, will make sure that the function (above) will be excuted on an update statement for an object. The problem is, how to call those functions in an object which extends the DataObject
/**
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
class TestimonialSuperclass
{
/**
* #ORM\PreFlush
*/
public function onPreFlush ()
{
echo 123;
}
}
/**
* #ORM\Entity
* #ORM\Table(name="testimonials")
* #ORM\HasLifecycleCallbacks
*/
class Testimonial extends TestimonialSuperclass
{
...
}

Injecting the Service Manager to Build a Doctrine Repository in ZF2

How do I inject the service manager into a Doctrine repository to allow me to retrieve the Doctrine Entity Manager?
I using the ZF2-Commons DoctrineORMModule and are trying to implement the repository example listed in the Doctrine Tutorial (bottom of tutorial in link below):
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
However, I keep getting a message "Fatal error: Call to a member function get() on a non-object in C:\zendProject\zf2 ... ", which suggests that I do not have a working instance of the service locator.
My Doctrine repository looks like this:
namespace Calendar\Repository;
use Doctrine\ORM\EntityRepository,
Calendar\Entity\Appointment,
Calendar\Entity\Diary;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApptRepository extends EntityRepository implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getUserApptsByDate()
{
$dql = "SELECT a FROM Appointment a";
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$query = $em()->createQuery($dql);
return $query->getResult();
}
}
I then want to call this in my controller using the following pattern:
$diary = $em->getRepository('Calendar\Entity\Appointment')->getUserApptsByDate();
EDIT: The attached link suggests that I may need to convert the class to a service,
https://stackoverflow.com/a/13508799/1325365
However, if this is the best route, how would I then make my Doctrine Entity aware of the service. At the moment I include an annotation in the doc block pointing to the class.
#ORM\Entity (repositoryClass="Calendar\Repository\ApptRepository")
The way i approach things is this:
First i register a Service for each entity. This is done inside Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'my-service-entityname' => 'My\Factory\EntitynameServiceFactory',
)
);
}
Next thing would be to create the factory class src\My\Factory\EntitynameServiceFactory.php. This is the part where you inject the EntityManager into your Entity-Services (not into the entity itself, the entity doesn't need this dependency at all)
This class looks something like this:
<?php
namespace My\Factory;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use My\Service\EntitynameService;
class EntitynameServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$service = new EntitynameService();
$service->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
return $service;
}
}
Next thing in line is to create the src\My\Service\EntitynameService.php. And this is actually the part where you create all the getter functions and stuff. Personally i extend these Services from a global DoctrineEntityService i will first give you the code for the EntitynameService now. All this does is to actually get the correct repository!
<?php
namespace My\Service;
class EntitynameService extends DoctrineEntityService
{
public function getEntityRepository()
{
if (null === $this->entityRepository) {
$this->setEntityRepository($this->getEntityManager()->getRepository('My\Entity\Entityname'));
}
return $this->entityRepository;
}
}
This part until here should be quite easy to understand (i hope), but that's not all too interesting yet. The magic is happening at the global DoctrineEntityService. And this is the code for that!
<?php
namespace My\Service;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
class DoctrineEntityService implements
ServiceManagerAwareInterface,
EventManagerAwareInterface
{
protected $serviceManager;
protected $eventManager;
protected $entityManager;
protected $entityRepository;
/**
* Returns all Entities
*
* #return EntityRepository
*/
public function findAll()
{
$this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entities' => $entities));
$entities = $this->getEntityRepository()->findAll();
$this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entities' => $entities));
return $entities;
}
public function find($id) {
return $this->getEntityRepository()->find($id);
}
public function findByQuery(\Closure $query)
{
$queryBuilder = $this->getEntityRepository()->createQueryBuilder('entity');
$currentQuery = call_user_func($query, $queryBuilder);
// \Zend\Debug\Debug::dump($currentQuery->getQuery());
return $currentQuery->getQuery()->getResult();
}
/**
* Persists and Entity into the Repository
*
* #param Entity $entity
* #return Entity
*/
public function persist($entity)
{
$this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entity'=>$entity));
$this->getEntityManager()->persist($entity);
$this->getEntityManager()->flush();
$this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entity'=>$entity));
return $entity;
}
/**
* #param \Doctrine\ORM\EntityRepository $entityRepository
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEntityRepository(EntityRepository $entityRepository)
{
$this->entityRepository = $entityRepository;
return $this;
}
/**
* #param EntityManager $entityManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
return $this;
}
/**
* #return EntityManager
*/
public function getEntityManager()
{
return $this->entityManager;
}
/**
* Inject an EventManager instance
*
* #param EventManagerInterface $eventManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEventManager(EventManagerInterface $eventManager)
{
$this->eventManager = $eventManager;
return $this;
}
/**
* Retrieve the event manager
* Lazy-loads an EventManager instance if none registered.
*
* #return EventManagerInterface
*/
public function getEventManager()
{
return $this->eventManager;
}
/**
* Set service manager
*
* #param ServiceManager $serviceManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
return $this;
}
/**
* Get service manager
*
* #return ServiceManager
*/
public function getServiceManager()
{
return $this->serviceManager;
}
}
So what does this do? This DoctrineEntityService pretty much is all what you globally need (to my current experience). It has the fincAll(), find($id) and the findByQuery($closure)
Your next question (hopefully) would only be "How to use this from my controller now?". It's as simple as to call your Service, that you have set up in the first step! Assume this code in your Controllers
public function someAction()
{
/** #var $entityService \my\Service\EntitynameService */
$entityService = $this->getServiceLocator()->get('my-service-entityname');
// A query that finds all stuff
$allEntities = $entityService->findAll();
// A query that finds an ID
$idEntity = $entityService->find(1);
// A query that finds entities based on a Query
$queryEntity = $entityService->findByQuery(function($queryBuilder){
/** #var $queryBuilder\Doctrine\DBAL\Query\QueryBuilder */
return $queryBuilder->orderBy('entity.somekey', 'ASC');
});
}
The function findByQuery() would expect an closure. The $queryBuilder (or however you want to name that variable, you can choose) will be an instance of \Doctrine\DBAL\Query\QueryBuilder. This will always be tied to ONE Repository though! Therefore entity.somekey the entity. will be whatever repository you are currently working with.
If you need access to the EntityManager you'd either only instantiate only the DoctrineEntityService or call the $entityService->getEntityManager() and continue from there.
I don't know if this approach is overly complex or something. When setting up a new Entity/EntityRepository, all you need to do is to add a new Factory and a new Service. Both of those are pretty much copy paste with two line change of code in each class.
I hope this has answered your question and given you some insight of how work with ZF2 can be organized.
As long as you extend the Doctrine\ORM\EntityRepository, you have immediate access to the entity manager by calling EntityRepository::getEntityManager() or the $_em attribute. The inheritence from the Doctrine\ORM\EntityRepository class allow you to do so.
Your method should now look like this:
public function getUserApptsByDate()
{
$dql = "SELECT a FROM Appointment a";
$em = $this->getEntityManager();// Or $em=$this->_em;
$query = $em()->createQuery($dql);
return $query->getResult();
}
I always keep in mind that access to my data should go from the web front (Zend MVC, Service Manager) to the persistence layer (Doctrine). My persistence (entities, repositories...) layer should not refer to the web front or neither know that it exists. If my system is doing the inverse at some level, then probably I'm doing something wrong.
Happy end of year