How to historize each version of my object with Doctrine ? - php

public function executeNew(sfWebRequest $request)
{
$this->form = new JobeetJobForm();
}
public function executeCreate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod('post'));
$this->form = new JobeetJobForm();
$this->processForm($request, $this->form);
$this->setTemplate('new');
}
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()));
if ($form->isValid())
{
$jobeet_job = $form->save();
$this->redirect('job/edit?id='.$jobeet_job['id']);
}
}
I generated module with doctrine generator. I would like make: if i edit current Job and click Save then instead of save this edit i would like create new object job with new ID and same data as current edited Job. How can i make it? I would like make this same as wikipedia.
EDIT:
i dont know how to open action edit, edit few fields and click Save and instead save this changes i would like create new object. What i must edit in processForm?

This is exactly the goal of the versionable behavior of Doctrine.

When you need do keep versions with all related objects. You can serialize the object and keep it in database. Look at the JMSerializerBundle at github.

Related

Symfony 3 - How to render and handle the same form in multiple actions in multiple controllers without duplicating too much code

Using symfony 3, i have multiple controllers and a number of actions all required to render and handle the same form. I'm sure there is a simpler and easier way to do this instead of repeating the form handling code 6 times in every action in every controller.
eg
Controller 1{
action1(){
//same form handling
}
action2(){
//same form handling
}
action3(){
//same form handling
}
action4(){
//same form handling
}
}
I was wondering if anyone could enlighten me as to how to do this. Thanks
You could just add some helper methods to your Controller
private function getForm()
{
// Create form
return $this->createForm(YourType::class);
}
private function handleForm(Form $form, Request $request)
{
// Handle the form
$form->handleRequest($request);
// Do some stuff
}
maybe you can create a service that can handle the request ...
so you will create your form in your controllers, actions, handle the request from your service, and create the view in the controllers, actions again to render it.
Seams feasible to me...
Hope this will help.
[Edit]
If you don't want to create a service only for this form,
you can :
mkdir a folder Handler in your bundle
create a file xxxHandler.php
inside it create a class xxxHandler like
class xxxHandler {
public function __construct(Form $form, Request $request, EntityManager $em, $session) {
$this->form = $form;
$this->request = $request;
$this->em = $em;
$this->session = $session;
}
public function process() {
if ($this->request->getMethod() == 'POST') {
$this->form->bindRequest($this->request);
if ($this->form->isValid()) {
$this->onSuccess($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(YourEntity $entity) {
$this->em->persist($entity);
$this->em->flush();
}
}
and in your controllers
instanciate a xxxHandler and give all parameters required. (don't forget use statements)
and call method proccess() in a 'if'
something like
$form = $this->createForm(new yourType, $yourEntity);
$formHandler = new ProspectHandler($form, $this->get('request'), $em, $session);
if ($formHandler->process()) {
//do wathever you want
}
PS: this is old symfony2 methods, modify it a little bit to make it work in symfony3

Laravel 5 Global CRUD Class

Before anyone asks, I've looked into CRUD generators and I know all about the Laravel Resource routes, but that's not exactly what I'm pulling for here.
What I'm looking to do is create one Route with a couple parameters, and one global class that (uses/extends?) the Model controller for simple CRUD operations. We have 20 or so Models and creating a Resource Controller for each table would be more time consuming than finding a way to create a global CRUD class to handle all "api" type calls and any ajax json request like a create / update / destroy statement.
So my question is what is the cleanest and best way to structure a class to handle all CRUD requests for every Model we have without having to have a resource controller for every model? I've tried researching this and can't seem to find any links except ones to CRUD generators and links describing the laravel Resource route.
The easiest way would be to do the following:
Add a route for your resource controller:
Route::resource('crud', 'CrudController', array('except' => array('create', 'edit')));
Create your crud controller
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Models\User;
use App\Models\Product;
use Input;
class CrudController extends Controller
{
const MODEL_KEY = 'model';
protected $modelsMapping = [
'user' => User::class,
'product' => Product::class
];
protected function getModel() {
$modelKey = Input::get(static::MODEL_KEY);
if (array_key_exists($modelKey, $this->modelsMapping)) {
return $this->modelsMapping[$modelKey];
}
throw new \InvalidArgumentException('Invalid model');
}
public function index()
{
$model = $this->getModel();
return $model::all();
}
public function store()
{
$model = $this->getModel();
return $model::create(array_except(Input::all(), static::MODEL_KEY));
}
public function show($id)
{
$model = $this->getModel();
return $model::findOrFail($id);
}
public function update($id)
{
$model = $this->getModel();
$object = $model::findOrFail($id);
return $object->update(array_except(Input::all(), static::MODEL_KEY));
}
public function destroy($id)
{
$model = $this->getModel();
return $model::remove($id);
}
}
Use your new controller :) You have to pass the model parameter that will contain the model key - it must be one of the allowed models in the whitelist. E.g. if you want to get a User with id=5 do
GET /crud/5?model=user
Please keep in mind that it's as simple as possible, you might need to make the code more sophisticated to match your needs.
Please also keep in mind that this code has not been tested - let me know if you see any typos or have some other issues. I'll be more than happy to get it running for you.
Unless you want to implement CRUD manually, consider to integrate a ready-made datagrid such as phpGrid.
Check out integration walkthrough: http://phpgrid.com/example/phpgrid-laravel-5-twitter-bootstrap-3-integration/ No models are required and the code is minimum. It can almost do anything.
A basic working CRUD:
// in a controller
public function index()
{
$dg = new \C_DataGrid("SELECT * FROM orders", "orderNumber", "orders");
$dg->enable_edit("FORM", "CRUD");
$dg->display(false);
$grid = $dg -> get_display(true);
return view('dashboard', ['grid' => $grid]);
}
You need one generic class for all CRUD operations and there are many ways to achieve that and one rule for all may not fit but you may try the approach that I'm going to describe now. This is an abstract idea, you need to implement it, so at first, think the URI for all CRUD operations. In this case you must follow a convention and it could be something like this:
example.com/user/{id?} // get all or one by id (if id is available in the URI)
example.com/user/create // Show an empty form
example.com/user/edit/10 // Show a form populated with User model
example.com/user/save // Create a new User
example.com/user/save/10 // Update an existing User
example.com/user/delete/10 // Delete an existing User
In ths case the user could be something else to specify the name of the model for example, example.com/product/create and keeping that on mind, you need to declare routes as given below:
Route::get('/{model}/{id?}', 'CrudController#read');
Route::get('/{model}/create', 'CrudController#create');
Route::get('/{model}/edit/{id}', 'CrudController#edit');
Route::post('/{model}/save/{id?}', 'CrudController#save');
Route::post('/{model}/delete/{id}', 'CrudController#delete');
Now, in your app\Providers\RouteServiceProvider.php file modify the boot method and make it look like this:
public function boot(Router $router)
{
$model = null;
$router->bind('model', function($modelName) use (&$model, &$router)
{
$model = app('\App\User\\'.ucfirst($modelName));
if($model)
{
if($id = $router->input('id'))
{
$model = $model->find($id);
}
return $model ?: abort(404);
}
});
parent::boot($router);
}
Then declare your CrudController as given below:
class CrudController extends Controller
{
protected $request = null;
public function __construct(Request $request)
{
$this->request = $request;
}
public function read($model)
{
return $model->exists ? $model : $model->all();
}
// Show either an empty form or a form
// populated with the given model atts
public function createOrEdit($model)
{
$classNameArray = explode('\\', get_class($model));
$className = strtolower(array_pop($classNameArray));
$view = view($className . '.form');
$view->formAction = "$className/save";
if(is_object($model) && $model->exists)
{
$view->model = $model;
$view->formAction .= "/{$model->id}";
}
return $view;
}
public function save($model)
{
// Validation required so do it
// Make sure each Model has $fillable specified
return $this->model->fill($this->request)->save();
}
public function delete($model)
{
return $this->model->delete();
}
}
Since same form is used to creating and updating a model, use something like this to create a form:
<form action="{{url($formAction)}}" method="POST">
<input
type="text"
class="form-control"
name="first_name" value="{{old('first_name', #$model->first_name)}}"
/>
<input type="Submit" value="Submit" />
{!!csrf_field()!!}
</form>
Remember that, each form should be in a directory corresponding to the model, for user add/edit, form should be in views/user/form.blade.php and for product model use views/product/form.blade.php and so on.
This will work and don't forget to add validation before saving a model and validation could be done inside the model using model events or however you want. This is just an idea but probably not the best way to it.

Linking a user to another entity on create in Symfony2

I am using the FOS_user bundle and when I create a user I want to link it to another Entity which is Company. I have tried to create a Listener using the following code:
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
);
}
public function onRegistrationSuccess(FormEvent $event) {
}
This is working good and I have access to $event in the function. Now, I want to access the name of the company that the user added in the form. Also, I want to create a company and bind it to the new user, so something like this:
$company = new Entity\Company();
$company->setTitle($theInputFromTheForm);
$user->addCompany($company);
I don't know how to access the data of the form and save the company in the user. Is using a listener the proper way to do it?
The event FOSUserEvents::REGISTRATION_SUCCESS receive a FormEvent object. You can get the registration form in your listener using $event->getForm()
So in you case;
public function onRegistrationSuccess(FormEvent $event) {
$registrationForm = $event->getForm();
$registrationFormData = registrationForm->getData();
}
I ended up creating a form type CompanyType that adds the title of my company. In my RegistrationFormType of my UserBundle I added a collection to this form type. I created a listener on FOSUserEvents::REGISTRATION_INITIALIZE to add an empty company.
public function onRegistrationInitialize(UserEvent $event) {
$user = $event->getUser();
$user->addCompany($company);
}

Populate entity from data array without form/request

Just wondering if it is possible to only use some parts of the symfony form handling. For exampe, when creating CRUD actions via generate:doctrine:crud I get something in my Controller (for handling create User POST requests) that looks like this:
$entity = new User();
$form = $this->createForm(new UserType(), $entity,
array(
'action' => $this->generateUrl('user_create'),
'method' => 'POST',
));
$form->handleRequest($request);
//Here I have a filled Entity
But what I want is to have this in a more reusable solution. Currently I have my business logic in a service called UserModel. Here I also want to have the create method to create, validate and persist a new entity. Tough the UserModel should also be callable from some Command scripts via the console, so I probably won't always have Request nor a Form.
So now from the above code I know that Symfony is already somehow populating data to an Entity according to the UserType definition, but how could I do that manually without having a Form or a Request and instead just some array of data?
So that I don't have to take care of setting the properties myself.
Edit:
The validation is no issue for populating the entity, I'm using the validator later on the populated entity before persisting the data.
And also important for me would be that the passed related entity ids will be handled/loaded automatically.
you may still use the Form component, but instead of using handleRequest, you should use directly submit.
If you are curious, you should look up the code on github for both handleRequest and what it actually does ; you'll see that it just do some verification, takes the data from the Request, and then uses the submit method of the Form.
So, basically, you can use only the submit method with the data you wish to use. It even validates your entity. :)
UPDATE
And for the concern of creating / updating related entities, if your relation have a persist / update cascade, it should roll out from itself without you doing a single thing, except persist + flush on your main entity.
If you do not worry about handling validation like things, you can do something like I have done.
You can define a trait or include the fromArray function in your entity classes.
trait EntityHydrationMethod
{
public function fromArray($data = array())
{
foreach ($data as $property => $value) {
$method = "set{$property}";
$this->$method($value);
}
}
}
If you defined trait, you can use it in your entities like:
class User{
use EntityHydrationMethod;
}
Then from your user model you can define your create function something like:
public function create($data = array())
{
$entity = new User();
$entity->fromArray($data);
return $entity;
}
-Updated-
As you updated your question. you may achieve this by defining a trait or include the createFromArray function in your EntityRepository classes.
trait RepositoryCreateMethod {
public function createFromArray($data)
{
$class = $this->getClassName();
$object = new $class();
$meta = $this->getClassMetadata();
foreach ($data as $property => $value) {
$v = $value;
if(!empty($value) && $meta->hasAssociation($property)) {
$map = $meta->getAssociationMapping($property);
$v = $this->_em->getRepository($map['targetEntity'])->find($value);
if(empty($v)){
throw new \Exception('Associate data not found');
}
}
$method = "set{$property}";
$object->$method($v);
}
return $object;
}
}
If you defined trait, you can use it in your Repository like:
class UserRepository{
use RepositoryCreateMethod;
}
Then you can use this something like call from controller:
$user = $this->getDoctrine()
->getRepository('YourBundle:User')
->createFromArray($data);

How to access the user object in the form configure method

I'm currently doing this kind fo thing with symfony forms
$this->myForm = new MyForm();
$this->myForm->customConfigureMethod($this->getUser()->getGuardUser());
because I need to configure a DoctriineChoice widget on the basis of the user.
I would rather do this kind of thing
$this->myForm =new myCustomConfiguredForm($this->getUser()->getGuardUser());
With the customisation being part of the form instantiation.
Anyone know how I could achieve this? I think I might be a bit unclear about the difference between the configure() and setup() functions for the forms so can't think clearly about it.
You shpuld pass the user object as an option. Here is an exapmle:
class ProductForm extends BaseProductForm
{
public function configure()
{
// or use an instance variable if you need the user in an another method too
$user = $this->getOption('user');
if (!$user instanceof sfBasicSecurityUser)
{
throw new InvalidArgumentException('A user object is required as "user" option in ' . __METHOD__);
}
// do something with the user...
}
}
$form = new ProductForm(array(), array('user' => $this->getUser()));

Categories