How can I protect my methods bodies (not the attached JavaDoc and Signature) using Acceleo code-generator - code-generation

I use Acceleo in order to generate code with a model I have made. I managed to protect my methods in order to protect them usinig "#generated NOT" in case I need to regenerate my code with Acceleo. The problem is that adding #generated NOT protect all the method content, that is to say the body, the signature and JavaDocs.
The thing is that I only need to keep the method body, or at least the method body and its signature, but I need the doc to be updated. How can I do this ?
Just for information here is an example of a potential generated class :
/*
* #generated
*/
public class ActeurRefEntrepriseServicesImpl implements ActeurRefEntrepriseServices {
#Autowired
HelloWorldService helloWorldService;
/**
* Service which say hello
*
* #param name
* user name
* #return print Hello username
*
* #generated NOT
*/
#Override
public void sayHello(final String name) {
helloWorldService.print(name);
}
}

Baptiste,
The #generated tags use the standard EMF protection rules : "#generated" means that the body of the block for which it is set will be generated, anything else means no re-generation. If you set something as "#generated" in any of your metamodels' generated code, you will see that there, too, the javadoc is preserved whatever the edits you do.
In short, you cannot tell EMF to re-generate anything other than the code itself.
If you need to have the body protected but not the javadoc, you have to shift from the "#generated" protection to Acceleo's [protected] blocks. i.e, change your template from :
[template generatedMethod(methodName : String)]
/**
* Some doc.
* #param param1
* param documentation.
* #generated
*/
[generateSignature(methodName)/] {
[generateBody()/]
}
[/template]
to something using a protected block :
[template generatedMethod(methodName : String)]
/**
* Some doc.
* #param param1
* param documentation.
*/
[protected (methodName)]
[generateSignature(methodName)/] {
[generateBody()/]
}
[/protected]
[/template]
With this paradigm, anything that is outside of the protected area will be regenerated, everything else will remain untouched by a regeneration.
See also the full documentation available from the Acceleo website.
If you absolutely need to use the "#generated" protection method for your model, you will need to tamper with the JMerger API from EMF and alter the launcher Acceleo generated for you in order to use your own merging strategy (see the getGenerationStrategy method from that launcher). Note that this is by no means an easy task.

Related

Why symfony FOSRest bundle doesn't find the right controller?

I have two actions:
/**
* #Rest\Get("/items/{itemId}")
*/
public function getAction(UuidInterface $id): View
And
/**
* #Rest\Get("/items/available")
*/
public function getAvailableAction() : View
The thing is that when I'm trying to call getAvailableAction by a link items/available, the getAction is being called. I guess it interprets the word available as an {itemId} somewhy.
How should I solve it?
You guessed right. Just define a proper requirement:
/**
* #Rest\Get("/items/{itemId}", requirements={"itemId" = "\d+"})
*/
If your itemId is an UUID, change the number regex from \d+ to [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12} or a simplified [a-fA-F0-9\-]{36}.
Don't forget to clear the cache.

Virtual properties in TYPO3 extbase domain models?

I'm trying to use a virtual domain model property in TYPO3 9.5.x that doesn't have a database field representation but I can't get it to work.
My model looks like this
class Project extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* participants
*
* #var string
*/
protected $participants;
...
/**
* Returns the participants
*
* #return string $participants
*/
public function getParticipants()
{
$this->participants = "foo";
return $this->participants;
}
}
I do see the property when I debug the model but it's always null as if it doesn't even recognise the getter method getParticipants().
Any idea what I might be doing wrong?
Already added a database field to ext_tables.sql and the TCA, but it didn't seem to make a difference.
The property is null because that's the state when the Extbase debugger inspects it. Notice that the Extbase debugger knows nothing about getters and also does not call them.
So if you want to initialize your property you must do this at the declaration time:
protected $participants = 'foo';
You can debug this property by simpy accessing it.
In Fluid, if you use <f:debug>{myModel}</f:debug>, you will see NULL for your property.
But if you directly use <f:debug>{myModel.participants}</f:debug>, you will see 'foo'.

ManyToOne with FOSUSerBundle ignoring exclusion policy

Building a JSON response for an API type thing, to retrieve a specific set of data that includes a ManyToOne relationship in the entity for my entity that extends FOSUSerBundle's User entity (called Account in my case).
The problem is, the Account entity thats included as a field in the response, is wanted, but I dont want to include all of the password and role type stuff.
I've been browing the internet for a couple hours now, and I've followed many guides on this, and I've cleared my cache every single time, and to no avail; So here's where I ended up:
// app/config/config.yml
jms_serializer:
metadata:
auto_detection: true
directories:
FOSUserBundle:
namespace_prefix: "FOS\\UserBundle"
path: "%kernel.root_dir%/Resources/serializer/FOS"
I've for below I've tried User.Model.yml and Model.User.yml and User.Entity.yml as well in a vain thought that the file name actually matters
// app/Resources/serializer/FOS/Entity.User.yml
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
id:
expose: true
and what I get still looks like this:
{
"status":"ok",
"api_version":"1.0",
"code":200,
"data":{
"video":{
"id":1,
"published":true,
"visibility":true,
"title":"Megaman 2",
"slug":"megaman-2",
"summary":"A rap song about Megaman",
"description":"A rap song\r\nAbout megaman",
"youtube_id":"R6L9bUouDr8",
"date_published":"2014-07-02T14:09:26-0700",
"date_created":"2014-07-02T14:09:26-0700",
"date_updated":"2014-07-02T14:09:26-0700",
"author_id":3,
"author":{
"id":3,
"username":"kharrison",
"username_canonical":"kharrison",
"email":"(sorry private)",
"email_canonical":"(sorry, private)",
"enabled":true,
"salt":"(sorry, private)",
"password":"(sorry, private)",
"last_login":"2014-07-04T15:17:34-0700",
"locked":false,
"expired":false,
"roles":[
"ROLE_SUPER_ADMIN"
],
"credentials_expired":false,
"display_name":"Kyle Harrison",
"slug":"kyle-harrison",
"bio":"Test"
}
}
}
}
The "author" field, is my Account entity thats being run through the JMSSerializer
I want to exclude ALL of that, except the user ID, Display name, and slug.
And finally this is how the API works:
// My/Bundle/Controller/BaseAPIController.php
//......... other code
/**
* #param string $status
* #param integer $code
* #return Response
*/
public function render_api($status, $code)
{
$this->apiResponse->setStatus($status);
$this->apiResponse->setCode($code);
return new Response($this->apiResponse->serialize($this->get('jms_serializer')), $this->apiResponse->getCode(), ["Content-type"=>"application/json"]);
}
//............. other code
and finally, that calls this:
// My/Bundle/Models
class APIResponse {
protected $status;
protected $apiVersion;
protected $code;
protected $data;
public function __construct($apiVersion, $status = "OK", $code = 500)
{
$this->status = $status;
$this->code = $code;
$this->apiVersion = $apiVersion;
$this->data = [];
}
// ... getters and setters
/**
* #return mixed
*/
public function serialize($serializer) {
return $serializer->serialize($this, "json");
}
}
I've for below I've tried User.Model.yml and Model.User.yml and
User.Entity.yml as well in a vain thought that the file name actually
matters.
It does matter, actually. It's a concatenation of the namespace and class name. In this case, you're trying to configure the FOS\UserBundle\Model\User class, so the file name should be Model.User.yml. (FOS\UserBundle\ should be excluded from the file name, since you configured it as namespace_prefix in your config.yml)
Also make sure that your Account class doesn't re-declare (overwrite) the properties, as the serializer config only works if you configure it for the class that actually declares the properties.
Ok So, the actual answer, couldn't have been arrived to via the information I provided. But Nic's Answer did lead me towards the solution. The description of how the the serializer looks at and deciphers the config file lead me to the real problem at hand.
This is what I failed to show:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends BaseUser
{
The problem lays with the Alias I provided the FOS\UserBundle\Model\User namespace. I no longer remember why I wrote that that way. However, the moment I remove the Alias and rewrote the extends to resemble this instead:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends User
{
combined with the new correct filename from Nic's answer, the config based Exclusion policy for JMSSerializerBundle totally kicks in, and every instance of FOSUserBundle's items are now completely hidden, except for the fields I've now explicitly told it to expose.
This is exactly what I wanted :)
Thanks everyone for your help! Cheers
~k
I'm not sure it's the exact way you want it, more a way around:
way around 1: Select only the properties you want (via the entity manager) and then serialize the array obtained.
It's what I do with what I call my API (which is not a class as you but controllers)

In an extbase extension, how to access the persistence layer from a scheduler task?

What sounds a bit academic in the title is actually quite straightforward: I have set up a TYPO3 6.1 extbase extension that I've equipped with a scheduler task. The task is supposed to import a CSV file and save it into the extension's database fields.
But how do I tell the scheduler task to use the extension's model etc. and save the received data into the persistence layer?
I've seen this answer to a similar question: Execute repository functions in scheduler task and I think it points the right way, but I think need a full example to start understanding how the dependency injection works.
First you have to consider the aspect of performance:
If you want to insert a big amount of data, you should not use the Extbase persistence for such a task. Because if you do so, it will generate an object for each row you want to insert and persist it immediately. This is quite slow and has a big memory footprint.
If you don't have much data or you split the jobs (e.g. perform 100 import jobs per scheduler run), then use the Extbase persistence.
You can have both in CommandController context, and since CommandControllers are straight-forward to set up, you should go for them instead of an own Scheduler task.
Using Extbase persistence
In the CommandController, inject your repository:
/**
* myRepository
*
* #var \Venor\Myext\Domain\Repository\MyRepository
* #inject
*/
protected $myRepository
Then iterate through the rows you want to import (foreach) and create a new object for every row and add it to your repository:
$myObject = $this->objectManager->get('Vendor\Myext\Domain\Model\MyModel');
$myObject->setProperty('foo');
$myObject->setOtherProperty('bar');
$this->myRepository->add($myObject);
To actually save the objects to the database, you need to persist them. So you also inject the persistenceManager:
/**
* #var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
* #inject
*/
protected $persistenceManager;
And use it:
$this->persistenceManager->persistAll();
You should not do that for every single object (for performance reasons); but for memory usage reasons you should not wait until after thousands of objects to persist, either. So you just insert an iterator to your foreach loop and persist every 20th, 40th, or whatever loop.
Please don't forget that the Scheduler works in Backend context, so the TypoScript must be available by module.tx_yourext. If you want to share the same settings/storagePid with the frontend part of your app, use
module.tx_yourext.persistence < plugin.tx_yourext.persistence
[...]
The TypoScript needs to be present in the root page of your website for backend modules/CommandControllers to use them. I suggest you add the stuff to myext/Configuration/TypoScript/setup.txt and add the static template of your extension to the root page.
Using DataHandler
The TYPO3 DataHandler (formerly TCEmain) is the engine the TYPO3 backend uses for inserting and modifying database records. It is very powerful.
Instead of an object, inside your loop you create an array containing all the data. The first array index is the table, the next level is the affected record, where NEW means that a new record is created. Then you can just set every field of a table with the desired value
$data = array();
$data['be_users']['NEW'] = array(
'pid' => 0,
'username' => $staffMember['IDPerson'],
'password' => md5(GeneralUtility::generateRandomBytes(40)), // random password
'usergroup' => '1,2',
'email' => $staffMember['Email'],
'realName' => $staffMember['Nachname'] . ' ' . $staffMember['Vorname'],
'lang' => 'de',
);
Now you can make an Instance of DataHandler and persist the changes:
/** #var $tce t3lib_TCEmain */
$tce = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
$tce->bypassAccessCheckForRecords = TRUE;
$tce->start($data, array());
$tce->admin = TRUE;
$tce->process_datamap();
$newRecordsUidArray = $tce->substNEWwithIDs['NEW'];
Please note the line $tce->admin = TRUE. This suggests to DataHandler that an admin is performing the action. This is convenient because you don't have to set allowed exclude fields for the Scheduler user and can also insert records to PID 0. But it is a possible security flaw, so carefully consider its usage.
Records inserted/updated by DataHandler logged correctly, can be reverted etc.. You can find some examples (such as adding pictures, resolving MM relations) here. In this case all DataHandler related functions were moved to an external repository class that is injected to the CommandController as described above (it's just named in Extbase convention).
A good overview of DataHandler functions can be found here.
In Addition to lorenz's answer: Beginner's Guide to set up a Command Controller Scheduler task:
My example is an import task. Change the Name part "Import" to your needs.
Create a new file EXT:Classes/Controller/ImportCommandController.php
<?php
namespace NAMESPACE\Myextension\Controller;
/***************************************************************
* Copyright notice
*
* (c) 2014
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
*
*
* #package Myextension
* #license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 or later
*
*/
class ImportCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\CommandController {
/**
* itemRepository
*
* #var \NAMESPACE\Myextension\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository;
/**
* #var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
* #inject
*/
protected $persistenceManager;
/**
*
* #param \integer $storagePid
* #param \string $url
*
* #return bool
*/
// very nice: parameters will be fields in the scheduler!
public function importCommand($storagePid = 0,$url = NULL) {
$source = utf8_encode(utf8_encode(file_get_contents($url)));
// set storage page ourselves
// not sure if really necessary
$querySettings = $this->itemRepository->createQuery()->getQuerySettings();
$querySettings->setRespectStoragePage(FALSE);
$this->itemRepository->setDefaultQuerySettings($querySettings);
// do your stuff here
$source = 'foo';
$rendered = 'bar';
// then store it
// this seems to be only necessary if we don't have an existing item yet
// but as we don't know that here, we have to do it
$item = $this->objectManager->get('STUBR\Therapiestellen\Domain\Model\Item');
// find all existing items
$all = $this->itemRepository->findAll();
// if we have an item already, take the first (and only one)
if(count($all) > 0){
$item = $all->getFirst();
}
// set / update properties
$item->setSource($source);
$item->setRendered($r);
$item->setPid($storagePid);
// either update or add it
if(count($all) > 0){
$this->itemRepository->update($item);
}
else {
$this->itemRepository->add($item);
}
// persist it
$this->persistenceManager->persistAll();
}
}
?>
In EXT:ext_localconf.php, add the command controller:
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'][] = 'NAMESPACE\\Myextension\\Controller\\ImportCommandController';
Configure in Scheduler:
That's basically it!

is it possible to override doctrine2 persistentobject magic getters and setting

Can anybody tell me whether its posible to override doctrine2 persistentobject magic getters\setters? i'd like to do the below:-
public function setDob($dob)
{
$this->dob= new \Date($date);
}
however my entity is defined as:-
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Ajfit\Repository\User")
* #ORM\HasLifecycleCallbacks
*/
class User extends \Doctrine\Common\Persistence\PersistentObject
{
/**
* #var date $dob
*
* #ORM\Column(name="dob", type="date")
*/
protected $dob;
}
the public function setDob does not get called when I create the entity using:-
public function getNewRecord() {
return $this->metadata->newInstance();
}
I get the below error:-
Notice:- array to string conversion ...Doctrine\DBAL\Statement.php on line 98
Any help would be much apprieciated.
Thanks
Andrew
__call of PersistentObject#__call will not be called if you defined the setDob method.
What you're doing there is creating a new instance via metadata. What you are doing there is probably assuming that __construct or any setter/getter should be called by the ORM. Doctrine avoids to call any methods on your object when generating it via metadata/hydration (check ClassMetadataInfo#newInstance to see how it is done) as it does only know it's fields.
This allows you to be completely independent from Doctrine's logic.
About the notice, that is a completely different issue coming from Doctrine\DBAL\Statement, which suggests me that you have probably some wrong parameter binding in a query. That should be handled separately.