How to make a full-featured backend without any line on client-side(html/css/js)
"A set of several rich open source bundles
based on Symfony2"
SonataPageBundle(CMS)
SonataMediaBundle(media library based on a dedicated abstract provider which handles different type of media: files, videos or images)
SonataNewsBundle(blog platform based on Doctrine2 and Symfony2)
SonataUserBundle(FOS/User integration)
SonataBlockBundle(block element rendering and caching using different cache storages (memcached/apc/esi/ssi/mongo))
{
"require": {
"sonata-project/admin-bundle": "~2.2@dev",
"sonata-project/block-bundle": "~2.2@dev",
"sonata-project/doctrine-orm-admin-bundle": "~2.2@dev",
"sonata-project/core-bundle": "~2.2@dev"
}
}
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
//...
new Sonata\CoreBundle\SonataCoreBundle(),
new Sonata\BlockBundle\SonataBlockBundle(),
new Sonata\jQueryBundle\SonatajQueryBundle(),
new Knp\Bundle\MenuBundle\KnpMenuBundle(),
new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
new Sonata\AdminBundle\SonataAdminBundle(),
//...
}
}
#config.yml
sonata_block:
default_contexts: [cms]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]
#routing.yml
admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin
_sonata_admin:
resource: .
type: sonata_admin
prefix: /admin
class Country
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=2)
*/
private $isoCode;
/**
* @ORM\Column(type="string", length=25)
*/
private $name;
}
class Index
{
/**
* @var string name
* @ORM\Column(type="string")
*/
private $name;
/**
* @var Country
* @ORM\ManyToOne(targetEntity="App\Entity\Country")
*/
private $country;
}
class Value
{
const VOLATILITY_LOW = 1;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\Column(type="integer")
*/
private $volatility = self::VOLATILITY_LOW;
/**
* @ORM\Column(type="float")
*/
private $fixed;
/**
* @ORM\Column(type="float")
*/
private $prognosed;
/**
* @ORM\Column(type="float")
*/
private $actual;
}
We even have fake data.
How to work with it?
namespace App\Admin;
use Sonata\AdminBundle\Admin\Admin;
class CountryAdmin extends Admin
{
}
#src/App/Resources/config/services.yml
parameters:
app.service.admin.country.class: App\Admin\CountryAdmin
services:
app.service.admin.country:
class: %app.service.admin.country.class%
tags:
- { name: sonata.admin, manager_type: orm, group: "Content", label: "Countries" }
arguments:
- ~
- App\Entity\Country
- ~
Menu
<?php
namespace App\Admin;
use Sonata\AdminBundle\Form\FormMapper;
class CountryAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('translations', 'a2lix_translations')
->add('isoCode');
}
}
app.service.admin.country:
class: %app.service.admin.country.class%
tags:
- { name: sonata.admin, manager_type: orm|doctrine_mongodb|doctrine_phpcr, group: "Content", label: "Countries" }
arguments:
- ~ #code
- App\Entity\Index #model- ~ #related CRUD controller
calls:
- [ setLabelTranslatorStrategy, ["@sonata.admin.label.strategy.underscore|noop|native|form_component|bc"]]
- [ setTranslationDomain, "some_domain"]
- [ setRouteBuilder, [ @sonata.admin.route.path_info|default_generator|query_string ]]
<?php
namespace App\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
class CountryAdmin extends Admin{
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('name')
->add('isoCode')
->add('_action', 'actions', [
'actions' => [
'show' => [],
'edit' => [],
'delete' => [],
]
]
);
}
}
<?php
use Sonata\AdminBundle\Form\FormMapper;
class IndexAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add(
'country',
'sonata_type_model',
[
'property' => 'name',
'required' => true
]
)
->add('enabled', null, ['required' => false]);
}
sonata_type_collection
By reference = false
Sonata options:
use Sonata\AdminBundle\Form\FormMapper;
class ValueAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('Meta')
->add('date', 'datetime')
->add(
'volatility',
'choice',
[
'choices' => Value::getVolatilityValues()
]
)
->add('index')
->end()
->with('Data')
->add('prognosed')
->add('actual')
->end()
->setHelps(
[
'prognosed' => 'Fixed value will be calculated automatically based on diff between actual and prognosed values ',
]
);
}
use Sonata\AdminBundle\Datagrid\ListMapper;
class IndexAdmin extends Admin
{
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('name')
->add('enabled', null, ['editable' => true])
->add(
'_action',
'actions',
[
'actions' => [
'show' => [],
'edit' => [],
'delete' => [],
]
]
);
}
}
High cost of deep customization
Documentation is messy and not always clear
Unclear strategy of releases
Rather difficult for newcomers
> 345000 installs on Packagist
> 900 stargazers
> 500 forks
~ 3000 commits
Sonata:
https://github.com/sonata-project
Sample app
https://github.com/akovalyov/SonataSimpleIndexes