phpdoc suggesting type for $this->someField - netbeans

In Netbeans and phpStorm,
this works as expected:
public function someMethod() {
$objectA = uberEnterprisyFactory('someclassA');
/* #var $objectA TheClassA */
// $objectA-> (autocomplete for TheClassA is displayed, good)
This does not:
public function someMethod() {
$this->objectA = uberEnterprisyFactory('somemodelA');
/* #var $this->objectA TheClassA */
// $this->objectA-> (no autocomplete here, not good, $this->objectA is inferred to be null)
How can I sugest type of $this->someThing to Netbeans and/or phpStorm?

Use the following PHPDoc annotation:
class MyClass {
/**
* #var MyPropertyClass
*/
private $myProperty
}

Related

How to get the autocomplition working correctly for inherited methods in PhpStorm?

There are two classes defined as follows:
class Foo
{
private $aaa;
public function setAaa(Aaa $aaa): self
{
$this->aaa = $aaa;
return $this;
}
}
class Bar extends Foo
{
private $bbb;
public function setBbb(Bbb $bbb): self
{
$this->bbb = $bbb;
return $this;
}
}
So here "fluent" setters are used. But PhpStorm seems to ignore this and displays a warning:
$bar = (new Bar())
->setAaa(new Aaa())
->setAaa(new Bbb())
;
Method 'setBbb' not found in ...\Foo
Is there a way to get the autocompletion working as expected ins such cases?
First of all -- fix your code sample -- make it real and not some PHP-looking chunk of text.
class Bar extends -- extends what?
what is setAaa() method?
what is setBbb() method? Your code sample does not have it.
Anyway ... as for the actual question, after making all the changes so it looks like real PHP code...
Use PHPDoc and ensure that it says #return $this. Right now it interprets self in : self part as specific class (which is Foo) ... and setPropertyBbb() is obviously NOT available in Foo class. By specifying #return $this you make it fluent in IDE eyes.
<?php
class Foo
{
private $aaa;
/**
* My super method
*
* #param Aaa $aaa
* #return $this
*/
public function setPropertyAaa(Aaa $aaa): self
{
$this->aaa = $aaa;
return $this;
}
}
class Bar extends Foo
{
private $bbb;
public function setPropertyBbb(Bbb $bbb): self
{
$this->bbb = $bbb;
return $this;
}
}
$bar = (new Bar())
->setPropertyAaa(new Aaa())
->setPropertyBbb(new Bbb())
;

vscode JavaScript - Reference types across files

How can I reference a type that is not exported (like MyClass) from another file in VSCode using JavaScript and CommonJS?
I have tried to use #module as dictated by JSDoc but it does not work. Is this not supported by VSCode?
Let's say I have two files:
myFactory.js:
/**
* #module MyModule
*/
class MyClass {}
const myFactory = {
create() {
return new MyClass();
}
}
module.exports = myFactory;
doSomething.js:
const myFactory = require('./myFactory');
/**
* #param {MyClass} item
*/
function doSomething1(item) {}
/**
* #param {MyModule.MyClass} item
*/
function doSomething2(item) {}
/**
* #param {module:MyModule.MyClass} item
*/
function doSomething3(item) {}
In doSomething.js, VSCode's Intellisence is not aware of MyClass so all 3 function signatures looks like this:
(local function) doSomethingX(item: any): void
Instead of
(local function) doSomethingX(item: MyClass): void
Here is my jsconfig.json:
{
"compilerOptions": {
"target": "es2017"
}
}
Try to move MyClass into separate file and set same #namespace for all files:
MyClass.js:
/** #namespace MyNamespace */
/** This is my class */
class MyClass {}
myFactory.js:
/** #namespace MyNamespace */
const myFactory = require('./MyClass');
const myFactory = {
create() {
return new MyClass();
}
}
module.exports = myFactory
doSomething.js:
/** #namespace MyNamespace */
const myFactory = require('./myFactory');
/** #param {MyClass} item */
function doSomething1(item) {}

Symfony 3.0.4 Circular reference detected during serialization with FOSRestBundle

I'm using FOSRestBundle in a Symfony project. When it I try to handle a view, it fails during the serialization of my data with the Symfony serializer as well as with the JMSSerializer.
This is the method rendering the response:
DefaultController.php
$em = $this->getDoctrine()->getManager('magellan');
$qb = $em->createQueryBuilder();
$query = $qb->select('h')
->from('DataBundle:Holding', 'h')
->where($qb->expr()->eq('h.id', ':holding_id'))
->setParameter('holding_id', $holding_id)
->getQuery();
$results = $query->getResult();
$view = $this->view($results, 200);
// Everything's ok up to this point
return $this->handleview($view);
And these are my entities:
Holding.php
class Holding
{
...
/**
* #ORM\OneToMany(targetEntity="Subsidiary", mappedBy="holding")
*/
private $subsidiaries;
}
Subsidiary.php
class Subsidiary
{
...
/**
* #ORM\ManyToOne(targetEntity="Holding", inversedBy="subsidiaries")
* #ORM\JoinColumn(name="id_holding", referencedColumnName="id_holding")
*/
private $holding;
/**
* #ORM\OneToMany(targetEntity="Brand", mappedBy="subsidiary")
*/
private $brands;
}
Brand.php
class Brand
{
...
/**
* #ORM\ManyToOne(targetEntity="Subsidiary", inversedBy="brands")
* #ORM\JoinColumn(name="id_subsidiary", referencedColumnName="id_subsidiary")
*/
private $subsidiary;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="brand")
*/
private $products;
}
Product.php
class Product
{
...
/**
* #ORM\ManyToOne(targetEntity="Brand", inversedBy="products")
* #ORM\JoinColumn(name="id_brand", referencedColumnName="id_brand")
*/
private $brand;
/**
* #ORM\ManyToOne(targetEntity="Sector", inversedBy="products")
* #ORM\JoinColumn(name="id_sector", referencedColumnName="id_sector")
*/
private $sector;
/**
* #ORM\OneToMany(targetEntity="Commercial", mappedBy="product")
*/
private $commercials;
}
Commercial.php
class Commercial
{
...
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="commercials")
* #ORM\JoinColumn(name="id_product", referencedColumnName="id_product")
*/
private $product;
/**
* #ORM\OneToMany(targetEntity="CommercialReport", mappedBy="commercial")
*/
private $reports;
CommercialReport.php
class CommercialReport
{
...
/**
* #ORM\ManyToOne(targetEntity="Commercial", inversedBy="reports")
* #ORM\JoinColumn(name="id_commercial", referencedColumnName="id_commercial")
*/
private $commercial;
}
Sector.php
class Sector
{
...
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="sector")
*/
private $products;
}
When using the default symfony serializer, I get the following error:
"message":"A circular reference has been detected (configured limit:
1).","class":"Symfony\Component\Serializer\Exception\CircularReferenceException"
And when using the JMSSerializer, when I go to the corresponding page of the controller, the page just never finishes loading. At the same time in the dev.log file new Doctrine.debug entries with requests to my DB are added every second.
$normalizers->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
Just add it after you make the instance if your objectNormalizer()
it worl perfectly for me
If you use FosRestBundle, you can use the GROUPS for the serializer. There is an annotation given by FosRestBundle : #FOS\RestBundle\Controller\Annotations\View(serializerGroups={"user"})
Your group can exclude the circular property.
Another idea you can do this. In your app/config/services.yml
circular_reference_handler:
public: false
class: callback
factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
arguments: ["#serializer.mapping.class_metadata_factory", null, "#serializer.property_accessor"]
public: false
tags: [serializer.normalizer]
calls:
- method: setCircularReferenceLimit
arguments: [1]
- method: setCircularReferenceHandler
arguments: ["#circular_reference_handler"]
The factory can be like this:
namespace AppBundle\Serializer;
class CircularHandlerFactory
{
/**
* #return \Closure
*/
public static function getId()
{
return function ($object) {
return $object->getId();
};
}
}

TYPO3 extbase: how to use ObjectStorage?

I'm trying to use a m:n relation, the same way as FrontEndUser is related to FrontEndUserGroup, e.g. without intermediate mm table. In my controller, I build my object, then I call $barRepository->update($barObject); to update the values of my object. However, it fails on the update function with the error:
Fatal error: Call to undefined method Cbrunet\Up\Domain\Model\Foo::getPosition() in /home/cbrunet/websites/typo3_src-6.1.1/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php on line 486
where Foo is the type of the object contained in the ObjectStorage of Bar. My understanding is that getPosition should be called on the ObjectStorage, not on the object contained into that ObjectStorage. However, I cannot figure out why this is not working in my case.
This is in TYPO3 6.1.5. Any hint would be appreciated.
The model of Bar which has a m:n relation to Foo looks like:
namespace Cbrunet\Up\Domain\Model;
class Bar extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Cbrunet\Up\Domain\Model\Foo>
*/
protected $myprop;
public function __construct() {
$this->myprop = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
/**
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $myprop
* #return void
*/
public function setMyprop(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $myprop) {
$this->myprop = $myprop;
}
/**
* #param \Cbrunet\Up\Domain\Model\Foo $myprop
* #return void
*/
public function addMyprop(\Cbrunet\Up\Domain\Model\Foo $myprop) {
$this->myprop->attach($myprop);
}
/**
* #param \Cbrunet\Up\Domain\Model\Foo $myprop
* #return void
*/
public function removeMyprop(\Cbrunet\Up\Domain\Model\Foo $myprop) {
$this->myprop->detach($myprop);
}
/**
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage
*/
public function getMyprop() {
return $this->myprop;
}
}
The relevant code in my controller looks like:
/**
* action update
*
* #return void
*/
public function updateAction() {
$args = $this->request->getArgument('myargs');
foreach ($args as $k=>$val) {
$pp = $this->barRepository->findOneByAprop($k); // another prop of Bar, not illustrated in the code above.
$listepour = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
foreach ($val as $p) {
$ap = $this->fooRepository->findOneByUid(intval($p));
$listepour->attach($ap);
}
$pp->setMyprop($listepour);
$this->barRepository->update($pp); // error occurs here
}
$this->redirect('list');
}
Do you also have configured your TCA?
do you have an initStorageObjects-function in your domain model?
Also you can try to build these case with the extension-manager and compare the code.

Doctrine 2 MongoDB ODM i18n

I'm playing with Doctrine ODM, trying to make some i18n-able field in my Mongo document. This is what I want to achieve in Mongo:
{
"title": {
"en": "Car",
"eu": "Autoa"
}
}
And the PHP API I want for the Document would be something like this:
$doc->getTitle()->setDefaultLocale('en');
$doc->getTitle(); // "Car"
$doc->getTitle()->get('eu'); // "Autoa"
$doc->getTitle()->set('es', 'Coche');
$doc->getTitle()->setTranslations([
'fr' => 'Voiture',
'eu' => 'Kotxea',
]);
$doc->getTitle()->getTranslations(); // ["en" => "Car", ...]
I have tried two aproaches, both of them with it's own pitfalls. I don't like none of them.
Custom Annotation
I've created a class which will be the middleman between the document and mongo. This class will be placed in the field, in this case in $title.
class Translation
{
protected $default;
protected $translations;
public function __construct(array $translations = array()) { /* ... */ }
public function get($locale) { /* ... */ }
public function getTranslations() { /* ... */ }
public function set($locale, $value) { /* ... */ }
public function setDefaultLocale($default) { /* ... */ }
public function setTranslations(array $translations = array()) { /* ... */ }
}
Then, I've created a custom FieldType, which converts the Mongo array to the Translation middleman object and viceversa (convertTo* methods seem to be ignored by Doctrine and are equal to the closureTo* methods, so I'll omit them):
class TranslationType extends \Doctrine\ODM\MongoDB\Types\Type
{
public function convertToDatabaseValue($value) { /* ... */ }
public function convertToPHPValue($value) { /* ... */ }
public function closureToMongo()
{
return '$return = $value->getTranslations();';
}
public function closureToPHP()
{
return '$return = new \App\TransBundle\MongoDB\Translation($value);';
}
}
Then, I have my annotation:
/** #Annotation */
class Translation extends \Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractField
{
public $type = 'translation';
}
And the document:
use App\TransBundle\MongoDB\Annotations\Translation;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** #MongoDB\Document */
class Translated
{
/** #MongoDB\Id */
protected $id;
/** #Translation */
protected $title;
public function getId() { /* ... */ }
public function getTitle() { /* ... */ }
}
The GOOD parts:
Easy usage: one use, property declaration, annotation and the getter.
Reads OK from Mongo => Doctrine.
Meets API requirements.
The BAD parts:
Doesn't save to DB, I suppose that it's because the Translation object doesn't dirty the title property on the parent object Translated.
$title doesn't get initialized to the middleman Translation object on object creation.
This would be fixed by initializing the object in the constructor, but if possible I'd like to try to avoid this to keep the usage as lean as possible. I'll have to find a workaround.
EmbedOne
The second approach consists of using an embedded document, this works perfectly, but has it's own small issues. :-)
First, my base Translation class for the embedded document, this class will work directly on the class properties instead of an array property:
class BaseTranslation
{
public function __construct(array $translations = array()) { /* ... */ }
public function get($locale) { /* ... */ }
public function getTranslations() { /* ... */ }
public function set($locale, $value) { /* ... */ }
public function setDefaultLocale($default) { /* ... */ }
public function setTranslations(array $translations = array()) { /* ... */ }
}
Then, the Translation class to be used in my projects, this will be the actual embbeded document:
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** #MongoDB\EmbeddedDocument */
class Translation extends BaseTranslation
{
/** #MongoDB\String */
protected $en;
/** #MongoDB\String */
protected $es;
/** #MongoDB\String */
protected $fr;
}
Finally the Document
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** #MongoDB\Document */
class Translated
{
/** #MongoDB\Id */
protected $id;
/** #MongoDB\EmbedOne(targetDocument="Translation") */
protected $title;
public function getId() { /* ... */ }
public function getTitle() { /* ... */ }
}
The GOOD parts:
It just works, reads and writes nicely.
Easy setup.
Can be used with any data type, so it would be easy to add an isTranslated i18n boolean field, just add a new TranslationBoolean class.
The BAD parts:
Not a big problem but the locales are hardcoded in the Translation class, it would be nice to be able to work directly on an array but this would add another level in the schema and type coercion might be lost.
Like in the other approach, the property doesn't get initialized, but it's easy as initializing it in the constructor (like any One2Many relation).
Conclusions
I'm liking more the second approach, which works how it's now. Do you have any idea how to overcome the BAD parts on both approaches?
Thanks!
Regarding the type methods:
closureToMongo() is actually not used.
closureToPHP() is used by the HydratorFactory class.
convertToPHPValue() is used by ClassMetadataInfo, but only for identifiers.
convertToDatabaseValue() is used in a few places, such as UnitOfWork and PersistenceBuilder.
The missing convertToDatabaseValue() implementation could be related to why you're seeing TranslationType fail to persist to the database.
The issue of "hardcoded locales in the Translation class" could be alleviated by storing an object (Hash annotation) within this field, although that would add another layer to the model. An EmbedMany array of objects containing a field each for the language code and value would consume even more space, although the model could make it easier to devise a form to edit the data.
Have you looked into the DoctrineExtensions library? It contains a component for having translatable documents/entities (supports both ODM and ORM). The author has slowed down his own development of it, but there are a healthy amount of pull requests being merged on the project and it is widely used.