Symfony - render twig code in controller - symfony

I need to build a customizer for my customers. They will be able to choose between multiple templates for their subdomain.
In order to do that, I took the following path :
Store templates in the DB with twig tags for the user's data
When needing to display a preview of a template, I would get it from the DB
Then I would render it in the controller and send the resulting HTML as a variable to the main template
I'm not succeeding into rendering in the controller. I tried several things, but the closest I got is this :
$loader = new Twig_Loader_Array(array(
'code.html' => $site->getTheme()->getCode(),
));
$twig = new Twig_Environment($loader);
$code = $twig->render('code.html', array( "test" => "CUSTOM DATA" ));
But I miss a use statement :
Attempted to load class "Twig_Loader_Array" from namespace "AppBundle\Controller". Did you forget a "use" statement for another namespace?
I'm not sure if it's the right path though.
So, if it is, please help me find out what use statement to use. More generally, how do you find what use statement should be used ?
If it's not the right strategy according to you, please feel free to explain me how dumb my idea was :)
[EDIT] So thanks to #DarkBee I found this solution that works, but I'm not sure whether I should do this or not :
use Twig\Loader\ArrayLoader;
use Twig\Environment;
...
$loader = new ArrayLoader(array(
'code.html' => $site->getTheme()->getCode(),
));
$twig = new Environment($loader);
$code = $twig->render('code.html', array( "test" => "CUSTOM DATA" ));
So if it's ok, great. If it's wrong (or again, if I'm wrong in the strategy choices), please tell me why and what would be better.

Related

Entity misbehaving whilst rendering PDF with twig, symfony2 and doctrine

I'm facing a funny issue here, I will do my best to explain it:
I have an Order entity and an orderProducts with a one to many relation.
Now I'm trying to generate a PDF invoice.
So I built my invoice using twig, then I use Knp\Snappy\Pdf; then it's just the following
$snappy = new Pdf($myProjectDirectory . 'vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386');
$renderedView = $this->renderView(
'ERPBundle:Orders:invoicepdf.html.twig', array(
'order' => $order,
'invoice' => $invoice
)
);
$snappy->generateFromHtml($renderedView, $invoicePath);
In the generated PDF, the orderProducts are duplicated, meaning I get the same row displayed twice.
I've rendered the template separately to view it in the browser and the orderProdcuts displays correctly, I used the same code to retrieve the data.
So I'm guessing this has got to be an issue between Snappy rendering of the html output + doctrine's lazy load. But I don't have the skills to debug this.
The issue is irrelevant to this code or the relative bundles.
The invoice PDF was generated after a form update, and in assigning my sub entity values I did this:
$orderProducts = $order->getOrderProducts();
foreach ($orderProducts as $orderProduct) {
$order->addOrderProduct($orderProduct);
}
This generated the duplicate value for orderProducts, I didn't catch this before, because doctrine recognizes the duplication and ignores it.
The fix is to properly handle the update like this
$orderProducts = $order->getOrderProducts();
foreach ($orderProducts as $orderProduct) {
$order->addOrderProduct($orderProduct);
if (empty($orderProduct->getId())) {
$order->addOrderProduct($orderProduct);
}
}
link to fix

Symfony2 get validation message

I have a few validation messages in the file validators.en.yml:
soporte.nombre.not_blank: The name cannot be empty
soporte.price.is_integer: The price should be integer
soporte.not_repeat: soporte cannot be repeat
I valid making a query to the database:
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= //here i need to get soporte.not_repeat that is on file validators.en.yml;
}
excuse me for my bad English, I used the translator.
If you just want to get the translation, this code should work :
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= $this->get('translator')->trans('soporte.not_repeat', array(), 'validators');
// Third param is the translation domain (first part of the translation file)
}
However, i suggest you to read this : http://symfony.com/doc/2.3/cookbook/validation/custom_constraint.html
If you want to follow Symfony best practices, you should create a custom constraint class/Validator and move your validation logic into it.
Best regards

Silverstripe Gridfield Relation

I am attempting to update the secure files silverstripe module to SS3.
In it, the author uses the following ComplexTableField:
class SecureFileTokenPermissionDecorator extends DataExtension {
static $has_many = array(
'AccessTokens' => 'SecureFileAccessToken'
);
....
$tokenList = new ComplexTableField(
$this->owner,
'ContainedFileTokens',
'SecureFileAccessToken',
null,
null,
"File.ParentID = '{$this->owner->ID}'",
$sourceSort = null,
"JOIN File ON FileID = File.ID"
));
$tokenList->setParentIdName('FolderID');
$tokenList->setRelationAutoSetting(false);
....
}
I was wondering how I would go about representing the same data/relation with gridfField.
Thanks!
Looking at the secure files module source code, I would suggest the following setup:
$tokenList = $gridField = new GridField(
'AccessTokens',
'Tokens',
$this->owner->AccessTokens(),
GridFieldConfig_RelationEditor::create()
);
This works directly on the relation getter, which is a lazy loaded list (not queried until necessary), and automatically paginated. I'm not quite sure how the setParentIdName("FolderID") fits in here, probably unnecessary. Caution: Haven't tried this on the actual codebase.
If you need some help understanding the API on a higher level, have a look in the GridField docs and the "datamodel" topic.
Thanks for helping to make modules ready for SS3! :)

How to use Silverstripe 3 beta UploadField

I am trying to use a UploadField on frontend for user to upload their company logo.
There isn't much documentation on UploadField yet. And I have tried it but no luck so far.
Can anyone guide me on how to use it?
This is a little old, but if anyone else stumbles upon this like I did.
UploadField does work frontend. I haven't been able to save into a many_many relationship using the saveInto function. But the biggest thing I missed was the DataObject/Page needs to exist first, as in it needs to be saved before you can attach a related object like an image.
static $has_one = array(
"Photo" => "Image"
);
$fields = new FieldList(
new UploadField( 'Photo', 'Upload' )
);
function saveForm( $data, $form ) {
$object = new DataObject();
// for a new object write before saveinto
$object->write();
$form->saveInto($object);
$object->write();
Director::redirectBack();
}
using ss 3.0.1
Alternatively rather than using the saveinto function you can manually loop over the parameters and attach them on the object yourself for many_many images.
The upload field checks for permissions via the can*() methods in the object.
In order to allow front end editing - you may have to overload File::canEdit (or Image::canEdit) in your custom object to handle this.

Symfony2 functional testing InvalidArgumentException: The current node list is empty

I get "InvalidArgumentException: The current node list is empty." running functional tests through PHPUnit. Here is test i wrote:
public function testAdd()
{
$client = static::createClientWithAuthentication('main');
$crawler = $client->request('GET', 'en/manage');
$send_button = $crawler->selectButton('submit');
$form = $send_button->form(array(
'PrCompany[email]' => 'test#example.ua',
'PrCompany[first_name]' => 'Anton',
'PrCompany[last_name]' => 'Tverdiuh',
'PrCompany[timezone]' => 'Europe/Amsterdam'
));
$form['PrCompany[companies][1]']->tick();
$client->submit($form);
$this->assertTrue($crawler->filter('html:contains("User is invited")')->count() > 0);
}
You can debug the problem by using var_dump($client->getResponse()->getContent());
Additionally, I think you should write this:
$crawler = $client->submit($form);
Otherwise you'll be testing the response of the first url, before form submission.
I was also struggling with this, and It appeared that the selectButton method triggered this error.
After reading up on the DOM Crawler docs I found that the selectButton method takes the actual button text as a string argument. So if your button is 'submit my form please', that will be your text.
It does take different parameters too, as shown below (taken from the docs)
A selectButton() method is available on the Crawler which returns another
Crawler that matches a button (input[type=submit], input[type=image],
or a button) with the given text.
EDIT
After finally successfully completing the test I would also recommend you follow this example for testing forms:
use Goutte\Client;
$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';
$crawler = $client->submit($form);
$this->assertTrue($crawler->filter('html:contains("Welcome Back")')->count() > 0);
The main difference being, I have used the Goutte bundle, which I installed with composer from the packagist (in my case I added "fabpot/goutte": "1.0.*#dev")
As a follow up to what #greg0ire wrote, check to see if
var_dump($client->getResponse()->getContent());
Returns a redirect page instead of the actual content. If so, you can add this:
$client->followRedirects(true);
I had the same problem with Silex application. I was looking for
$buttonCrawler = $crawler->selectButton('input[type="submit"]');
Instead, the correct way to do it is give the value of the button
$buttonCrawler = $crawler->selectButton('value_of_the_button');
For example, in your page:
<form>
...
<input type="submit" value="Click Me">
</form>
And in your tests:
$buttonCrawler = $crawler->selectButton('Click Me');
$form = $buttonCrawler->form();
...
I see question still dont have answer. I had the same problem.
In my case goutte was not able to do this request because input name is changed by javascript on the fly.
When goutte received html it saw one form. And when submitting with pre filled params, form input elements could not be matched by $form->setValues($params) so \InvalidArgumentException was thrown.
Solved by doing request by hand.
// $form->setValues($data);
// $this->getGoutte()->submit($form);
$data = array(
'input_name[key]' => 'value'
);
$this->getGoutte()->request($form->getMethod(), $form->getUri(), $params);
You can try to use Codeception with Symfony2 module. It provides flexible interface to Symfony2 functional tests and has better debugging features.
This error would occur when crawler can't find form element requested; Quite tricky when you are using, for instance, form builder as when run, it will create different input name:
$form = $this-> createFormBuilder($store)
->add('storeNumber','text')
->add('storeName','text')
->add('save', 'submit')
->getForm();
will output field name like:
form_storeNumber
which should be used in test class:
$form=$crawler->selectButton('save')->form();
$form['form_storeNumber']='10';

Resources