PhpStorm - setting setters and getters - phpstorm

PhpStorm has a useful feature when quickly creating setters and getters:
Code -> generate -> select the items to generate
This is great for setting basic setters/getters.
However from time to time I want to set more than just setters and getters. For instance I may want to create setters and getters for a one to many or many to many relationship.
i.e.
Many to many
/**
* {#inheritDoc}
*/
public function getOwner()
{
return $this->owner->toArray();
}
/**
* Set the list of user
* #param Collection $owner
*/
public function setOwner( Collection $owner )
{
$this->registeredUsers->clear();
foreach ( $owner as $item ) {
$this->owner[] = $item;
}
}
/**
* Add merchant to the collection
* #param Collection $owner
*/
public function addOwner( Collection $owner )
{
foreach( $owner as $item ) {
$this->owner->add( $item );
}
}
/**
* #param Collection $owner
*/
public function removeOwner( Collection $owner )
{
foreach( $owner as $item ) {
$this->owner->removeElement( $item );
}
}
Is there a way to do this via the same code generation process?

Unfortunately ATM PhpStorm does not support such functionality.
https://youtrack.jetbrains.com/issue/WI-25003 -- watch this ticket (star/vote/comment) to get notified about any progress (so far it's not planned for any specific future version -- possibly because of very low number of votes -- sort of: no votes = no interest = no need to invest resources into it right now).
Related: https://youtrack.jetbrains.com/issue/WI-19891
The only alternative I can think of right now (that would use that Code Generation functionality) .. is to modify template for Getter/Setter so that it contains more than one method (example). This way you can generate setXXX/addXXX as well as getXXX/removeXXX methods in one go.
Obviously, it would be applied in all cases, so if you need just get/set then you would need to manually remove add/remove methods -- that's a drawback of such approach.
Note that File & Code Templates could be IDE-wide (Default schema) or project-specific (Project schema) .. so you may use such combined getters/setters in certain projects only.
https://www.jetbrains.com/help/phpstorm/2016.1/file-and-code-templates-2.html
The only other alternative is half manual and requires you to use Live Templates.
Make actual live template (one time job)
You need to manually find the place where you want this code to be placed
Invoke Live Template expansion (so that code template gets inserted)
Fill all live template variables to complete the code.
With your addXXX/removeXXX code sample you may end up with just filling 1 or 2 template variables (just a quick estimation on what I see; the same variable can be used multiple times so will be filled in few places at the same time; entered text can be transformed (limited set of transformations) so it can be re-used in another variable automatically (e.g. you are typing owner and in another place it is used as Owner automatically).

Check this out:
#set ($array = $TYPE_HINT.split('\|'));
#set ($IS_COLLECTION = false);
#set ($FIXED_RETURN_TYPE = $RETURN_TYPE);
#set ($NAME_LENGTH = $FIELD_NAME.length());
#set ($NAME_BEFORE_LAST = $FIELD_NAME.length() - 1);
#set ($LAST_CHARACTER = $FIELD_NAME.substring($NAME_BEFORE_LAST, $NAME_LENGTH));
#set ($METHOD_NAME_BEFORE_LAST = $NAME.length() - 1);
#if (${LAST_CHARACTER} == 's')
#set ($FIXED_PROPERTY_NAME = $FIELD_NAME.substring(0, $NAME_BEFORE_LAST));
#set ($FIXED_METHOD_NAMING_PART = $NAME.substring(0, $METHOD_NAME_BEFORE_LAST));
#else
#set ($FIXED_PROPERTY_NAME = $FIELD_NAME);
#set ($FIXED_METHOD_NAMING_PART = $NAME);
#end
#foreach($hint in $array)
#if ($hint.toString().contains('[]'))
#set ($ENTRY_HINT = $hint.toString().replace('[]', ''));
#set ($HAS_ENTRY_HINT = true);
#end
#end
#foreach($hint in $array)
#if ($hint == 'Collection')
#set ($IS_COLLECTION = true)
#set ($FIXED_RETURN_TYPE = 'Collection')
#break
#elseif ($hint == 'ArrayCollection')
#set ($IS_COLLECTION = true)
#set ($FIXED_RETURN_TYPE = 'Collection')
#break
#elseif ($hint == 'array')
#set ($IS_COLLECTION = true)
#set ($FIXED_RETURN_TYPE = 'array')
#elseif ($hint.toString().contains('[]'))
#set ($IS_COLLECTION = true)
#set ($FIXED_RETURN_TYPE = 'array')
#end
#end
#if ($IS_COLLECTION)
/**
* #param ${ENTRY_HINT} $${FIXED_PROPERTY_NAME}
*
* #return ${CLASS_NAME}
*/
public function add${FIXED_METHOD_NAMING_PART}(#if (${HAS_ENTRY_HINT})${ENTRY_HINT} #else#end$${FIXED_PROPERTY_NAME})#if(${RETURN_TYPE}): ${CLASS_NAME}#else#end
{
#if ($FIXED_RETURN_TYPE == 'Collection')
if(false === $this->${FIELD_NAME}->contains($${FIXED_PROPERTY_NAME})) {
$this->${FIELD_NAME}->add($${FIXED_PROPERTY_NAME});
}
#else
if(false === in_array($${FIXED_PROPERTY_NAME}, $this->${FIELD_NAME}, true)) {
$this->${FIELD_NAME}[] = $${FIXED_PROPERTY_NAME};
}
#end
return $this;
}
/**
* #param ${ENTRY_HINT} $${FIXED_PROPERTY_NAME}
*
* #return ${CLASS_NAME}
*/
public function remove${FIXED_METHOD_NAMING_PART}(#if (${HAS_ENTRY_HINT})${ENTRY_HINT} #else#end$${FIXED_PROPERTY_NAME})#if(${RETURN_TYPE}): ${CLASS_NAME}#else#end
{
#if ($FIXED_RETURN_TYPE == 'Collection')
if(true === $this->${FIELD_NAME}->contains($${FIXED_PROPERTY_NAME})) {
$this->${FIELD_NAME}->removeElement($${FIXED_PROPERTY_NAME});
}
#else
if(true === in_array($${FIXED_PROPERTY_NAME}, $this->${FIELD_NAME}, true)) {
$index = array_search($${FIXED_PROPERTY_NAME}, $this->${FIELD_NAME});
array_splice($this->${FIELD_NAME}, $index, 1);
}
#end
return $this;
}
#end
/**
* #param ${TYPE_HINT} $${PARAM_NAME}
* #return ${CLASS_NAME}
*/
public function set${NAME}(#if (${SCALAR_TYPE_HINT})${SCALAR_TYPE_HINT} #else#end$${PARAM_NAME})#if(${RETURN_TYPE}): ${CLASS_NAME}#else#end
{
$this->${FIELD_NAME} = $${PARAM_NAME};
return $this;
}
I've managed to create something like that, feel free to modify upon your needs. This is the fluent setter live template.
It works with Doctrine's Collection/ArrayCollection, arrays. Extending this functionality is as simple as creating new #elseif statement.
/**
* #var array|string[]
*/
protected $oneField;
/**
* #var ArrayCollection|Collection|Another[]
*
* #ORM\OneToMany(targetEntity="Acme\DemoBundle\Entity\Another")
*/
protected $anotherField;

Related

How to exchange values between 2 records at unique column

How to exchange the values in the Unique column of two records?
like below Model
User
id
name
code // this is unique
What I want to do is like...
$user1 = User::find(1);
$user2 = User::find(2);
DB::Transaction();
try {
$user1->code = $user2->code;
$user2->code = $user1->code;
$user1->save();
$user2->save();
} catch()...
of course, I know this code doesn't work by constraint violation error.
Anyway,I want to exchange UserA's code and UserB's code.
But I don't know the best way.
Any one knows?
I tried this code,and it worked.but it seems little dirty.
$user1 = User::find(1);
$user2 = User::find(2);
DB::Transaction();
try {
$user1_code_temp = $user1->code;
$user2_code_temp = $user2->code;
$user1->code = rand();
$user1->save();
$user2->code = $user1_code_temp;
$user2->save();
$user1->code = $user2_code_temp
$user1->save();
} catch()...
What you are doing is correct. And I personally would prefer it being this way as it is more readable what you are doing. You could disable unique checks in mysql temporarily but I would not recommend that.
To clean up the code, you can add this as a method in users model :
<?php
// Inside User.php model
/**
* Exchange user code
*
* #param User $user
*
* #return void
*/
public function exchangeCode(User $user){
$code1 = $this->code;
$code2 = $user->code;
$this->setCode(rand(6));
$user->setCode($code1);
$this->setCode($code2);
return $this;
}
/**
* Set Code
*
* #param string $code
*
* #return void
*/
public function setCode($code)
{
$this->code = $code;
$this->save();
return $this;
}
// And then in controller
$user1->exchangeCode($user2);
On your model define a
protected $primaryKey = 'code';

A circular reference has been detected (configured limit: 1) Serializer SYMFONY [duplicate]

I am using Doctrine 2 and Zend framework since a few days.
I am generating my entities across yaml files.
Now I met an issue to convert my entities Doctrine into Json format (in order to use it through AJAX).
Here is the code used :
$doctrineobject = $this->entityManager->getRepository('\Entity\MasterProduct')->find($this->_request->id);
$serializer = new \Symfony\Component\Serializer\Serializer(array(new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer()), array('json' => new Symfony\Component\Serializer\Encoder\JsonEncoder()));
$reports = $serializer->serialize($doctrineobject, 'json');
below is the return I get :
Fatal error: Maximum function nesting level of '100' reached, aborting! in /Users/Sites/library/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php on line 185
the issue seems to be the same than here :
http://comments.gmane.org/gmane.comp.php.symfony.symfony2/2659
but there is not proper solution proposed.
Any idea how can I do it ?
Cheers
I solved the same problem by writing my own GetSetNormalizer my class. Defined static variable in a class for branching
class LimitedRecursiveGetSetMethodNormalizer extends GetSetMethodNormalizer
{
public static $limit=2;
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null)
{
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = strtolower(substr($method->name, 3));
$attributeValue = $method->invoke($object);
if (null !== $attributeValue && !is_scalar($attributeValue) && LimitedRecursiveGetSetMethodNormalizer::$limit>0) {
LimitedRecursiveGetSetMethodNormalizer::$limit--;
$attributeValue = $this->serializer->normalize($attributeValue, $format);
LimitedRecursiveGetSetMethodNormalizer::$limit++;
}
$attributes[$attributeName] = $attributeValue;
}
}
return $attributes;
}
/**
* Checks if a method's name is get.* and can be called without parameters.
*
* #param ReflectionMethod $method the method to check
* #return Boolean whether the method is a getter.
*/
private function isGetMethod(\ReflectionMethod $method)
{
return (
0 === strpos($method->name, 'get') &&
3 < strlen($method->name) &&
0 === $method->getNumberOfRequiredParameters()
);
}
}
And usage
LimitedRecursiveGetSetMethodNormalizer::$limit=3;
$serializer = new Serializer(array(new LimitedRecursiveGetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$response =new Response($serializer->serialize($YOUR_OBJECT,'json'));
JMSSerializerBundle seems to handle circular references fine.

Update discriminator column Doctrine2 with Symfony2

I have an entity called User which has inheritance for Student, Professional and Business.
When a user is registered, is only a User but they must update their profile and choose which kind of user is, I have a form which handles this, a controller which gets the form data, but I can't update the discriminator field type with $userEntity->setType()
This is my mapping stuff
class User
{
const TYPE_BASIC = "Basico";
const TYPE_STUDENT = "Estudiante";
const TYPE_PROFESSIONAL = "Profesional";
const TYPE_BUSINESS = "Empresa";
protected $type = self::TYPE_BASIC;
public function getType()
{
return self::TYPE_BASIC;
}
public function setType($type)
{
$this->type = $type;
}
class Student extends User
{
protected $type = self::TYPE_STUDENT;
And then Professional and Business just like Student (changing const)
<entity name="User" table="user_base" inheritance-type="JOINED">
<discriminator-column name="type" type="string"/>
<discriminator-map>
<discriminator-mapping value="Basico" class="User"/>
<discriminator-mapping value="Estudiante" class="Student"/>
<discriminator-mapping value="Profesional" class="Professional"/>
<discriminator-mapping value="Empresa" class="Business"/>
</discriminator-map>
the child tables are named user_xxx where xxx = Student/Professional/Business
And this is my controller
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$data = $form->all();
$type = $data['type']->getData();
$email = $data['email']->getData();
$profile = $data['profile']->all();
$name = $profile['name']->getData();
$lastName = $profile['lastName']->getData();
$birth = $profile['birth']->getData();
$profileEntity = new Profile();
$profileEntity->setBirth($birth);
$profileEntity->setName($name);
$profileEntity->setLastName($lastName);
$profileEntity->setUser($user);
$em->persist($profileEntity);
ladybug_dump($type);
$userEntity = $em->getRepository('User')->find($user);
$userEntity->setProfile($profileEntity);
$userEntity->setType($type);
if($user->getEmail() != $email)
$userEntity->setEmail($email);
$em->persist($userEntity);
$em->flush();
}
Everything is persisted but type field, which remains it's original data. I know when I change discriminator column I need to create a new row inside it's child element, but first I want to know how to change the discriminator column.
it is possible if you use this custom bit of code in the Form of a Trait which you can use inside a Repository.
The Trait:
namespace App\Doctrine\Repository;
use App\Exception\InvalidDiscriminatorClassException;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Discriminator Trait
*/
trait DiscriminatorTrait
{
/**
* #return ClassMetadata
*/
abstract public function getClassMetadata();
/**
* #return EntityManager
*/
abstract public function getEntityManager();
/**
* Update Discriminator Column
*
* #param integer $id
* #param string $class
* #return boolean
* #throws InvalidDiscriminatorClassException
*/
private function updateDiscriminatorColumn($id, $class)
{
/* #var ClassMetadata $classMetadata */
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new InvalidDiscriminatorClassException($class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
/* #var Connection $connection */
$connection = $this->getEntityManager()->getConnection();
try {
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
catch (DBALException $e) {
return false;
}
return true;
}
}
According to the Doctrine documentation on Inheritance mapping, it is not possible to either get or set the type. You may wish take advantage of PUGXMultiUserBundle, which readily handles the mapping. This bundle also makes it possible for your users to register with the appropriate profile.

How to declare a EventSubscriber in config.yml using bazinga_geocoder.geocoder service?

I am planning to make a reverse geocoding based on the BazingaGeocoderBundle. A simple way to do that is write this simple code in the controller:
$result = $this->container
->get('bazinga_geocoder.geocoder')
->using('google_maps')
->reverse(48.79084170157100,2.42479377175290);
return $this->render("MinnAdsBundle:Motors:test.html.twig",
array('result'=>var_dump($result)));
Until here, things are going well.
My objective is to make the code nicer & resuable. So, I used this article to write my own GeocoderEventSubscriber as describer below:
<?php
namespace Minn\AdsBundle\Doctrine\Event;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\LifecycleEventArgs;
//use Geocoder\Provider\ProviderInterface;
use Bazinga\Bundle\GeocoderBundle\Geocoder\LoggableGeocoder;
/**
* Subscribes to Doctrine prePersist and preUpdate to update an
* the address components of a MotorsAds entity
*
* #author majallouli
*/
class MotorsAdsGeocoderEventSubscriber implements EventSubscriber {
protected $geocoder;
public function __construct(LoggableGeocoder $geocoder){
$this->geocoder = $geocoder;
}
/**
* Specifies the list of events to listen
*
* #return array
*/
public function getSubscribedEvents(){
return array(
'prePersist',
'preUpdate',
);
}
/**
* Sets a new MotorsAds's address components if not present
*
* #param LifecycleEventArgs $eventArgs
*/
public function prePersist(LifecycleEventArgs $eventArgs){
$motorsAds = $eventArgs->getEntity();
if($motorsAds instanceof \Minn\AdsBundle\Entity\MotorsAds){
if( !$motorsAds->getCountry()){
$em = $eventArgs->getEntityManager();
$this->geocodeMotorsAds($motorsAds,$em);
}
}
}
/**
* Sets an updating MotorsAds's address components if not present
* or any part of address updated
*
* #param PreUpdateEventArgs $eventArgs
*/
public function preUpdate(PreUpdateEventArgs $eventArgs){
$motorsAds = $eventArgs->getEntity();
if($motorsAds instanceof \Minn\AdsBundle\Entity\MotorsAds){
if( !$motorsAds->getCountry() ){
$em = $eventArgs->getEntityManager();
$this->geocodeMotorsAds($motorsAds,$em);
$uow = $em->getUnitOfWork();
$meta = $em->getClassMetadata(get_class($motorsAds));
$uow->recomputeSingleEntityChangeSet($meta, $motorsAds);
}
}
}
/**
* Geocode and set the MotorsAds's address components
*
* #param type $motorsAds
*/
private function geocodeMotorsAds($motorsAds,$em){
$result = $this->geocode
->using('google_maps')
->reverse($motorsAds->getLat(),$motorsAds->getLng());
$motorsAds->setCountry(
$em->getRepository("MinnAdsBundle:Country")->findCountryCode($result['countryCode']));
}
}
After that, I declared my EventSubscriber as a service:
services:
# ...
geocoder_motorsads.listener:
class: Minn\AdsBundle\Doctrine\Event\MotorsAdsGeocoderEventSubscriber
arguments: [#bazinga_geocoder.geocoder] # almost sure that the error is here!!
tags:
- { name: doctrine.event_subscriber }
Actually, I get this error:
ContextErrorException: Notice: Undefined property: Minn\AdsBundle\Doctrine\Event\MotorsAdsGeocoderEventSubscriber::$geocode in /home/amine/NetBeansProjects/tuto/src/Minn/AdsBundle/Doctrine/Event/MotorsAdsGeocoderEventSubscriber.php line 78
I am almost sure that error is in the declaration of arguments of the EventSubscriber. Is it #bazinga_geocoder.geocoder?
Thank you for your help!
Your property is $this->geocoder but you're calling $this->geocode, you're spelling it wrong.

Zend Framework v1 INSERT INTO multiple rows

Im working on E-Shops based on Zend Framework v1.12 and I need to create a parser of product list with thousands of records. I'd tried to make a loop of createRow() -> save() but I found it too slow with big lists.
Does Zend fr. have an functional to save multiply record in one query?
No, Zend Framework v1 doesn't have such functional. But it's not so hard to make an implementation of INSERT INTO for your needs. Here is my function:
/**
* #brief Safe implementation of INSERT INTO
*
* #param [in] $array Array[] of values 'column'=>'value"
* #return void
*
*/
protected function saveRows($array) {
$values=$columns = array();
$vLabels=$cLables = '';
foreach ($array as $colval) {
$vLabels.="(";
foreach ($colval as $column=>$value) {
if (!$ready) {
array_push($columns,$column);
$cLables.='?,';
}
array_push($values,$value);
$vLabels.='?,';
}
$vLabels = rtrim($vLabels,', ');
!$ready ? $cLables = rtrim($cLables,', ') : null;
$vLabels .= "),";
$ready = true;
}
$vLabels = rtrim($vLabels,', ');
$query="INSERT INTO `".$this->primary_key."` (".$cLables.") VALUES ".$vLabels;
$subst=array_merge($columns,$values);
$this->query($query,$subst);
}