How to add PunkAveFileUploaderBundle to sonata admin bundle - symfony

Well...Everything is in the title and here I am because the documentation about this topic doesn't exist really..So I hope some ninja developers will be able to give me some tips...
I'm working on my personal portfolio (Symfony 2.3) and I have a problem since a couple of week. I used sonata admin bundle to create my admin panel and have a lot of troubles to fix the different file upload in my admin. For that I was wondering to use the PunkAveFileUploaderBundle. But honestly I have no idea of how to implement it properly. I think I have to edit some sonata admin files but here again...which ones? I'm reading the files and have already a good idea of which ones to tweak but not sure at all.. By reading and following the documentation of sonata admin (sonata doc) I never had success.. Don't know why I'm actually following it step by step..
Well if anyone of you has an idea of how to implement the PunkAveFileUpload bundle with sonata admin bundle, let me know your tips or even better, a small example...
PS: links to the documentation are not needed, thank you.

May be you can read this post about getting Gedmo Uploadable working with Sonata Admin
I think you'll have to pass the punk_ave.file_uploader service in your admin class:
acme.admin.demo:
class: Acme\DemoBundle\Admin\DemoAdmin
arguments: [~, Acme\DemoBundle\Entity\Demo, SonataAdminBundle:CRUD, #punk_ave.file_uploader]
tags:
- {name: sonata.admin, manager_type: orm, group: demo, label: demo}
calls:
- [ setTranslationDomain, [SonataAdminBundle]]
ANd change your DemoAdmin class to manage uploads:
class DemoAdmin extends Admin
{
/**
* File uploader
*/
private $fileUploader = null;
/**
* Constructor
*/
public function __construct($code, $class, $baseControllerName, $fileUploader = null)
{
parent::__construct($code, $class, $baseControllerName);
$this->fileUploader = $fileUploader;
}
// ...
public function prePersist($object)
{
$this->manageUploads($object);
}
public function preUpdate($object)
{
$this->manageUploads($object);
}
/**
* Mannger uploads
* #param Demo $object
*/
private function manageUploads($object)
{
if ($object->getId()->getFile()) {
$this->fileUploader->syncFiles(...);
}
}
}
I really don't know if this will work, but that's the way I'll try to make it works...

Related

Can't use Route

Route("/) does not work.
I already tried to reinstall annotations and Symfony with Composer.
My routes.yaml :
index:
path: /
controller: App\Controller\ArticleController::homepage
My ArticleController.php :
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ArticleController
{
/**
* #Route("/")
*/
public function homepage()
{
return new Response("TEST");
}
/**
* #Route("/news/test-test")
*/
public function show()
{
return new Response("FUTURE");
}
}
The page always shows the public/index.php, but it should show "FUTURE" ;)
in your config folder you should find a folder called routes inside it should be a file called annotations.yaml which contains the following :
controllers:
resource: ../../src/Controller/
type: annotation
if you have this you should get "TEST" when you go to ("/")
and you get "FUTURE" when you go to (/news/test-test)
and make sure to clear the cache if it did not
and you have to comment every thing inside routes.yml
Like Ali Mhanna said, check first annotations.yaml.
Then, according to Symfony documentation (read it, it can help ;), run php bin/console debug:router
If you see your route, your route is working.

Sonata ecommerce custom product admin

I am using sonata e commerce bundle and I have added a couple of products. I can get them list and displayed in the admin section but when I try to create a new product I only see the basic field that all the products have.
Is there any way to create an Admin class that will allow me to see the extra fields for each kind of product class?
I finally figured out how to do this looking into the sonata sandbox example.
To add the custom fields to the product in the admin I have to override the WineProductProvider class and add the following method just like an admin class.
/**
* {#inheritDoc}
*/
public function buildEditForm(FormMapper $formMapper, $isVariation = false)
{
parent::buildEditForm($formMapper, $isVariation);
$formMapper
->with('Bottle details')
->add('origin','text')
->add('year','integer')
->add('grapes','text')
->add('closure','text')
->add('food','text')
->add('style','text')
->add('size','integer');
$formMapper->end();
}
And thats it. Hope it helps someone else.

My custom block doesn't show up in the block library

I am developing a custom module in Drupal 8. It shows data regarding some organizations that make use of our service. For this I have created a Controller that shows data from the database, which is put there by another module. From the scarce information and tutorials available on Drupal 8 developement I've been able to create the following. In the .routing.yml file I have created a path to this overview table like so (it doesn't properly copy here but the indents are okay):
OrganizationOverview.world:
path: '/world'
defaults:
_controller: 'Drupal\OrganizationOverview\Controller\OrganizationOverviewController::overview'
_title: 'World'
requirements:
_role: 'administrator'
_permission: 'access content'
So now the overview is accessible with the URL site.com/world. But what we want is to show it on the frontpage or show it anywhere else on the site. For this it needs to be a Block. For this I have created an OrganizationOverviewBlock class in OrganizationOverview/src/Plugin/Block/OrganizationOverviewBlock.php which is the proper way according to the PSR-4 standard. The class looks like this:
<?php
namespace Drupal\OrganizationOverview\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Session\AccountInterface;
/**
* Provides a 'OrganizationOverviewBlock' block.
*
* #Block(
* id = "organization_overview_block",
* admin_label = #Translation("OrganizationOverviewBlock"),
* category = #Translation("Custom")
* )
*/
class OrganizationOverviewBlock extends BlockBase
{
public function build()
{
return array(
'#markup' => 'Hello World',
);
}
public function blockAccess(AccountInterface $account)
{
return $account->hasPermission('access content');
}
}
So now it should show up in the Blocks Layout page (after flushing cache, which I do consistently) at site.com/admin/structure/block/ as "Organization Overview Block" where I should enable it, according to plenty sources (Create custom Block, Block API Drupal 8). But it doesn't show up there. I've tried implementing ContainerFactoryPluginInterface with some of those methods but that changes nothing. It does not show up. I've tried making a new test module with a block with the same code but a simpler name and it does not show up. I've copied the code to another platform (the production site) but it also doesn't show up there. What am I doing wrong? Can someone help me? I know Drupal 8 is new but this module really needs to be published soon.
You'll find a working example of building custom block in the Drupal Examples Project. So:
Get the Drupal 8 examples project
Enable the Block Example Module
Double check the working code
With that, you should get your block available in your own module
You can also take advantage of what explained here, where a single php file do the all job. Check files and folders path also.
Not require routing file for custom block.
<pre>
class TestBlock extends BlockBase {
/*
** {#inheritdoc}
*/
public function build() {
return array(
'#markup' => $this->t('Welcome page!'),
);
}
}
</pre>
http://drupalasia.com/article/drupal-8-how-create-custom-block-programatically
You should respect the Drupal coding standard recommendations:
No camelCase naming convention in module name.
OrganizationOverview actually is an error, you should use organization_overview (lowercase/underscore) naming conventions.

How to get child object in embedded Admin class in Sonata Admin?

I'm trying to get and manipulate the actual object related to a ImageAdmin class in SonataAdmin (using Symfony 2.3). This works fine when the ImageAdmin class is the only one being used. But when ImageAdmin is embedded in another Admin it goes horribly wrong.
Here's what works when you don't have embedded Admins:
class ImageAdmin extends Admin {
protected $baseRoutePattern = 'image';
protected function configureFormFields(FormMapper $formMapper) {
$subject = $this->getSubject();
}
}
But when you embed ImageAdmin in ParentAdmin using this:
class PageAdmin extends Admin {
protected function configureFormFields(FormMapper $formMapper) {
$formMapper->add('image1', 'sonata_type_admin');
}
}
Then when you're editing a Parent item with id 10 and call getSubject() in ImageAdmin you get the Image with id 10!
In other words getSubject() extracts the id from the URL then calls $this->getModelManager()->find($this->getClass(), $id);, which cross-references the Parent id and the Image id. Oops!
So... what I want to do is be able to get hold of the actual object that is being rendered/edited in the current ImageAdmin instance, whether it's being edited directly or via an embedded form, and then be able to do things with it.
Maybe getSubject() is the wrong tree to be barking up, but I note that $this->getCurrentChild() returns false when called from ImageAdmin::configureFormFields(), even when that ImageAdmin is embedded using the sonata_type_admin field type. I'm quite confused...
Anyway, I hope it is possible to get hold of the object in some obvious way that I've overlooked and somebody here can help enlighten me!
Thanks to Tautrimas for some ideas, but I managed to figure out an answer to this:
In ImageAdmin set this:
protected function configureFormFields(FormMapper $formMapper)
{
if($this->hasParentFieldDescription()) { // this Admin is embedded
$getter = 'get' . $this->getParentFieldDescription()->getFieldName();
$parent = $this->getParentFieldDescription()->getAdmin()->getSubject();
if ($parent) {
$image = $parent->$getter();
} else {
$image = null;
}
} else { // this Admin is not embedded
$image = $this->getSubject();
}
// You can then do things with the $image, like show a thumbnail in the help:
$fileFieldOptions = array('required' => false);
if ($image && ($webPath = $image->getWebPath())) {
$fileFieldOptions['help'] = '<img src="'.$webPath.'" class="admin-preview" />';
}
$formMapper
->add('file', 'file', $fileFieldOptions)
;
}
I'll post this in the upcoming SonataAdmin cookbook soon!
https://github.com/sonata-project/SonataAdminBundle/issues/1546
caponica's solution is working only on oneToOne relations, am I right? In my oneToMany case , this: $parent->$getter() returns a collection, and I don't know how to identify the current subject.
I've found this bug report:
https://github.com/sonata-project/SonataAdminBundle/issues/1568, which contains a fix for this, but it is still open, so I hope they merge it soon:(
Edit
With some research there is a temporary fix for this: Fixed getting wrong subject in sonata_type_collection
In short:
create a class and copypaste the content of this file: AdminType
then add this to your services.yml, and change the class namespace to you new class namespace:
services:
sonata.admin.form.type.admin:
class: ACME\AdminBundle\Form\Type\AdminType
tags:
- { name: form.type, alias: sonata_type_admin }
It still has a bug though:
also fix doesn't work when enabled cascade_validation in the parent docment and embedded form has errors
Can you try $this->getForm()->getViewData(); within your ImageAdmin? This should get you the correct child entity.
I tried all these solutions, but none proved to work.
So, I worked to find a solution. My solution is based on caponica's solution, but work on oneToMany case. Tha solution I found is a workaround, but works good.
It's working using the session.
public function getCurrentObjectFromCollection($adminChild)
{
$getter = 'get' . $adminChild->getParentFieldDescription()
->getFieldName();
$parent = $adminChild->getParentFieldDescription()
->getAdmin()
->getSubject();
$collection = $parent->$getter();
$session = $adminChild->getRequest()->getSession();
$number = 0;
if ($session->get('adminCollection')) {
$number = $session->get('adminCollection');
$session->remove('adminCollection');
}
else {
$session->set('adminCollection', 1 - $number);
}
return $collection[$number];
}
And you get the correct object in the admin by:
$object = $this->getCurrentObjectFromCollection($this)
So, when the parent needs to show the list of child admins, each child admin will run this function and will update the session parameter. When all the elements have been taken, the session parameter is deleted.
This code is made for lists with only 2 elements, but can be updated for any number of elements.
Hope this helps somebody :)
I had same problem and i am able to do this through "Custom Form Type Extension" for which documentation is given on the link "http://symfony.com/doc/current/cookbook/form/create_form_type_extension.html" .
It is the perfect solution ..

Symfony2: Global configurable backend prefix for all bundles?

I'm looking for a global configuration for a routing prefix. There are a number of different bundles and I need a path to the backend controllers.
/**
* #Route("/admin/post")
*/
class PostAdminController extends Controller {
}
When I configure it that way, I have to edit a lot of files, if the prefix should be changed. Is it possible, to use a variable or a filter?
/**
* #Route("/%BACKEND_PREFIX%/post")
*/
class PostAdminController extends Controller {
}
Somewhere in configuration:
backend_prefix = admin
Ok, now I found a solution, but it seems working since version 2.1.
http://symfony.com/doc/current/cookbook/routing/service_container_parameters.html

Resources