When creating a bundle, special attention should be paid to one parameter - the choice of configuration type. The symphony offers four different options - yml, xml, php, or annotation. But the real choice is between yml and annotation, that is, between the choice of configuration in separate YAML format files, or in the format of annotations placed directly in the comments in the code of the controllers and entities. On this account, many copies were broken in this topic , there are arguments in both versions. My choice in this case is annotations, because the project is very small, and the advantages of individual configuration files are leveled (in fact, only one file will have a configuration — a doctrine essence). The type of configuration does not affect the performance of the bundle itself in production - in any case, the entire config is cached.
<?php namespace Lexxpavlov\PageBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; /** * @ORM\Entity */ class Page { /** * @var integer * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * @Gedmo\Slug(fields={"title"}, updatable=false) * @ORM\Column(type="string", length=100, unique=true) */ protected $slug; /** * @var string * * @ORM\Column(type="string", length=255) */ protected $title; /** * @var string * @ORM\Column(type="text") */ protected $content; /** * @var string * @ORM\Column(type="text", name="keywords", nullable=true) */ protected $keywords; /** * @var string * @ORM\Column(type="text", name="description", nullable=true) */ protected $description; /** * @var boolean * @ORM\Column(type="boolean", options={"default":false}) */ protected $published = false; /** * @var \Datetime * @Gedmo\Timestampable(on="create") * @ORM\Column(type="datetime", name="created_at") */ protected $createdAt; /** * @var \Datetime * @Gedmo\Timestampable(on="update") * @ORM\Column(type="datetime", name="updated_at") */ protected $updatedAt; public function __toString() { return $this->title ?: 'n/a'; } /** * Get id * @return integer */ public function getId() { return $this->id; } /** * Set slug * @param string $slug * @return Page */ public function setSlug($slug) { $this->slug = $slug; return $this; } /** * Get slug * @return string */ public function getSlug() { return $this->slug; } /** * Set title * @param string $title * @return Page */ public function setTitle($title) { $this->title = $title; return $this; } /** * Get title * @return string */ public function getTitle() { return $this->title; } /** * Set content * @param string $content * @return Page */ public function setContent($content) { $this->content = $content; return $this; } /** * Get content * @return string */ public function getContent() { return $this->content; } /** * Set meta keywords * @param string $keywords * @return Page */ public function setKeywords($mkeywords) { $this->keywords = $keywords; return $this; } /** * Get meta keywords * @return string */ public function getKeywords() { return $this->keywords; } /** * Set meta description * @param string $description * @return Page */ public function setDescription($description) { $this->description = $description; return $this; } /** * Set meta description * @return string */ public function getDescription() { return $this->description; } /** * Set published * @param boolean $published * @return Page */ public function setPublished($published) { $this->published = $published; return $this; } /** * Toggle published * @return Page */ public function togglePublished() { $this->published = !$this->published; return $this; } /** * Get published * @return boolean */ public function getPublished() { return $this->published; } /** * Sets created at * @param DateTime $createdAt * @return Page */ public function setCreatedAt(\DateTime $createdAt) { $this->createdAt = $createdAt; return $this; } /** * Returns created at * @return DateTime */ public function getCreatedAt() { return $this->createdAt; } /** * Sets updated at * @param DateTime $updatedAt * @return Page */ public function setUpdatedAt(\DateTime $updatedAt) { $this->updatedAt = $updatedAt; return $this; } /** * Returns updated at * @return DateTime */ public function getUpdatedAt() { return $this->updatedAt; } }
stof_doctrine_extensions: orm: default: sluggable: true timestampable: true
<?php namespace Lexxpavlov\PageBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class PageType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('title', 'text') ->add('slug', 'text', array('required' => false)) ->add('content', 'textarea') ->add('published', 'checkbox', array('required' => false)) ->add('keywords', 'text', array('required' => false)) ->add('description', 'text', array('required' => false)) ->add('save', 'submit') ; } public function getName() { return 'lexxpavlov_page'; } }
services: lexxpavlov_page.form.type.page: class: Lexxpavlov\PageBundle\Form\Type\PageType tags: - { name: form.type, alias: lexxpavlov_page }
// ... $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml');
<?php namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; use Lexxpavlov\PageBundle\Entity\Page; /** * @Route("/page") */ class DefaultController extends Controller { /** * @Route("/", name="page") * @Template() */ public function indexAction() { $pages = $this->getDoctrine()->getManager() ->getRepository('LexxpavlovPageBundle:Page') ->findAll(); return array( 'pages' => $pages, ); } /** * @Route("/show/{slug}", name="page_show") * @Template() */ public function showAction(Page $page) { } /** * @Route("/new", name="page_new") * @Template() */ public function newAction(Request $request) { $page = new Page(); $form = $this->createForm('lexxpavlov_page', $page); if ($request->isMethod('POST')) { $form->handleRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($page); $em->flush(); return $this->redirect($this->generateUrl('page')); } } return array( 'form' => $form->createView(), ); } }
{# src/Lexxpavlov/PageBundle/Resources/views/Default/index.html.twig #} <ul> {% for page in pages %} <li><a href="{{ path('page_show', {slug: page.slug}) }}">{{ page.title }}</a></li> {% endfor %} </ul> <a href="{{ path('page_new') }}"> </a> {# src/Lexxpavlov/PageBundle/Resources/views/Default/show.html.twig #} <article> <h1>{{ page.title }}</h1> <div> : <time datetime="{{ page.createdAt|date('Ym-d') }}" pubdate>{{ page.createdAt|date('dmY') }}</time></div> {{ page.content|raw }} </article> {# src/Lexxpavlov/PageBundle/Resources/views/Default/new.html.twig #} <h1> </h1> {{ form(form) }}
<?php namespace Lexxpavlov\PageBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Show\ShowMapper; class PageAdmin extends Admin { public function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('title') ->add('slug') ->add('published', null, array('editable' => true)) ->add('createdAt', 'datetime') ->add('updatedAt', 'datetime') ; } public function configureFormFields(FormMapper $formMapper) { $formMapper ->with('General') ->add('slug', null, array('required' => false)) ->add('title') ->add('content') ->add('published', null, array('required' => false)) ->end() ->with('SEO') ->add('keywords', null, array('required' => false)) ->add('description', null, array('required' => false)) ->end() ; $formMapper->setHelps(array( 'slug' => 'Leave blank for automatic filling from title field', )); } public function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('slug') ->add('title') ->add('published') ; } public function configureShowFields(ShowMapper $showMapper) { $showMapper ->add('slug') ->add('title') ->add('content') ->add('published') ->add('publishedAt', 'datetime') ->add('createdAt', 'datetime') ->add('updatedAt', 'datetime') ->add('keywords') ->add('description') ; } }
services: sonata.admin.lexxpavlov_page: class: Lexxpavlov\PageBundle\Admin\Page tags: - { name: sonata.admin, manager_type: orm, group: "Content", label: "Pages", label_catalogue: "messages" } arguments: - ~ - Lexxpavlov\PageBundle\Entity\Page - ~ calls: - [ setTranslationDomain, [messages]]
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); $loader->load('admin.yml');
<?php namespace Lexxpavlov\PageBundle\Listener; use Gedmo\Sluggable\SluggableListener as BaseSluggableListener; use Gedmo\Sluggable\Util\Urlizer; class SluggableListener extends BaseSluggableListener { public function __construct(){ $this->setTransliterator(array($this, 'transliterate')); } public function transliterate($text, $separator = '-') { $convertTable = array( '' => 'a', '' => 'b', '' => 'v', '' => 'g', '' => 'd', '' => 'e', '' => 'e', '' => 'zh', '' => 'z', '' => 'i', '' => 'j', '' => 'k', '' => 'l', '' => 'm', '' => 'n', '' => 'o', '' => 'p', '' => 'r', '' => 's', '' => 't', '' => 'u', '' => 'f', '' => 'h', '' => 'ts', '' => 'ch', '' => 'sh', '' => 'sch', '' => '', '' => 'y', '' => '', '' => 'e', '' => 'yu', '' => 'ya' ); $text = strtr(trim(mb_strtolower($text, 'UTF-8')), $convertTable); return Urlizer::urlize($text, $separator); } }
stof_doctrine_extensions: orm: default: sluggable: true timestampable: true class: sluggable: Lexxpavlov\PageBundle\Listener\SluggableListener
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Lexxpavlov\PageBundle\Entity\Page as BasePage; /** * @ORM\Entity */ class Page extends BasePage { }
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Lexxpavlov\PageBundle\Entity\Page as BasePage; use AppBundle\Entity\User; /** * @ORM\Entity */ class Page extends BasePage { /** * @var User * @ORM\ManyToOne(targetEntity="User") * @Gedmo\Blameable(on="create") */ protected $createdBy; /** * @var User * @ORM\ManyToOne(targetEntity="User") * @Gedmo\Blameable(on="update") */ protected $updatedBy; /** * Set user, that updated entity * @param User $updatedBy * @return Page */ public function setUpdatedBy($updatedBy) { $this->updatedBy = $updatedBy; return $this; } /** * Get user, that updated entity * @return User */ public function getUpdatedBy() { return $this->updatedBy; } /** * Set user, that created entity * @param User $createdBy * @return Page */ public function setCreatedBy($createdBy) { $this->createdby = $createdBy; return $this; } /** * Get user, that created entity * @return User */ public function getCreatedBy() { return $this->createdBy; } }
<?php namespace AppBundle\Entity; use FOS\UserBundle\Model\User as BaseUser; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="users") */ class User extends BaseUser { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * Get id * @return integer */ public function getId() { return $this->id; } }
stof_doctrine_extensions: orm: default: sluggable: true timestampable: true blameable: true
security: # ... access_control: - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/page/new, role: ROLE_ADMIN }
<?php namespace Lexxpavlov\PageBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('lexxpavlov_page'); $rootNode ->children() ->scalarNode('entity_class') ->cannotBeEmpty() ->end() ->end() ; return $treeBuilder; } }
lexxpavlov_page: entity_class: AppBundle\Entity\Page
$container->setParameter('lexxpavlov_page.entity_class', $config['entity_class']);
services: sonata.admin.lexxpavlov_page: class: Lexxpavlov\PageBundle\Admin\Page tags: - { name: sonata.admin, manager_type: orm, group: "Content", label: "Pages", label_catalogue: "messages" } arguments: - ~ - %lexxpavlov_page.entity_class% - ~ calls: - [ setTranslationDomain, [messages]]
services: lexxpavlov_page.form.type.page: class: Lexxpavlov\PageBundle\Form\Type\PageType arguments: [ %lexxpavlov_page.entity_class% ] tags: - { name: form.type, alias: lexxpavlov_page }
<?php namespace Lexxpavlov\PageBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class PageType extends AbstractType { private $dataClass; public function __construct($dataClass) { $this->dataClass = $dataClass; } public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('title', 'text') ->add('slug', 'text', array('required' => false)) ->add('content', 'textarea') ->add('published', 'checkbox') ->add('keywords', 'text') ->add('description', 'text') ->add('save', 'submit') ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => $this->dataClass, )); } public function getName() { return 'lexxpavlov_page'; } }
$rootNode ->children() ->scalarNode('entity_class') ->cannotBeEmpty() ->end() ->scalarNode('admin_class') ->defaultValue('Lexxpavlov\PageBundle\Admin\PageAdmin') ->end() ->scalarNode('content_type') ->defaultValue('ckeditor') ->end() ->end() ;
public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); $container->setParameter('lexxpavlov_page.entity_class', $config['entity_class']); $container->setParameter('lexxpavlov_page.content_type', $config['content_type']); if ($config['admin_class'] && $config['admin_class'] != 'false') { $loader->load('admin.yml'); $container->setParameter('lexxpavlov_page.admin_class', $config['admin_class']); } }
services: sonata.admin.lexxpavlov_page: class: %lexxpavlov_page.admin_class% tags: - { name: sonata.admin, manager_type: orm, group: "Content", label: "Pages", label_catalogue: "messages" } arguments: - ~ - %lexxpavlov_page.entity_class% - ~ calls: - [ setTranslationDomain, [messages]] - [ setContentType, [ %lexxpavlov_page.content_type% ] ]
<?php namespace Lexxpavlov\PageBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Show\ShowMapper; class PageAdmin extends Admin { protected $contentType = 'ckeditor'; // or null for simple textarea field public function setContentType($contentType) { $this->contentType = $contentType; } public function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('title') ->add('slug') ->add('published', null, array('editable' => true)) ->add('createdAt', 'datetime') ->add('updatedAt', 'datetime') ; } public function configureFormFields(FormMapper $formMapper) { $formMapper ->with('General') ->add('slug', null, array('required' => false)) ->add('title') ->add('content', $this->contentType) ->add('published', null, array('required' => false)) ->end() ->with('SEO') ->add('keywords', null, array('required' => false)) ->add('description', null, array('required' => false)) ->end() ; $formMapper->setHelps(array( 'slug' => 'Leave blank for automatic filling from title field', )); } public function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('slug') ->add('title') ->add('published') ; } public function configureShowFields(ShowMapper $showMapper) { $showMapper ->add('slug') ->add('title') ->add('content') ->add('published') ->add('publishedAt', 'datetime') ->add('createdAt', 'datetime') ->add('updatedAt', 'datetime') ->add('keywords') ->add('description') ; } }
<?php namespace Lexxpavlov\PageBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class PageType extends AbstractType { private $dataClass; public function __construct($dataClass) { $this->dataClass = $dataClass; } public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('slug', 'text') ->add('title', 'text') ->add('content', $options['contentType']) ->add('published', 'checkbox') ->add('keywords', 'text') ->add('description', 'text') ->add('save', 'submit') ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => $this->dataClass, 'contentType' => 'ckeditor', )); } public function getName() { return 'lexxpavlov_page'; } }
{ "name" : "lexxpavlov/pagebundle", "description" : "Symfony2 Page bundle with meta data, predefined form type and Sonata Admin service", "version" : "1.0.0", "type" : "symfony-bundle", "homepage": "https://github.com/lexxpavlov/PageBundle", "license" : "MIT", "keywords" : ["page", "page bundle"], "authors" : [{ "name" : "Alexey Pavlov", "email" : "lexx.pavlov@gmail.com" }], "require" : { "php": ">=5.3.2", "symfony/symfony": ">=2.1", "stof/doctrine-extensions-bundle": ">=1.1" }, "suggest": { "egeloen/ckeditor-bundle": "Allow use ckeditor field" }, "autoload" : { "psr-4" : { "Lexxpavlov\\PageBundle\\" : "" } }, "extra" : { "branch-alias" : { "dev-master" : "1.0.x-dev" } } }
$ cd /path/to/project/src/Lexxpavlov/PageBundle $ git init $ git add . -A $ git commit -m "Init commit"
$ git remote add origin remote https://github.com/yourusername/YourBundle.git $ git push origin master
[...] "require" : { [...] "lexxpavlov/pagebundle" : "dev-master" }, "repositories" : [{ "type" : "vcs", "url" : "https://github.com/lexxpavlov/PageBundle.git" }], [...]
$ git tag 1.0.0 $ git push origin --tags
[...] "require" : { [...] "lexxpavlov/pagebundle" : "1.0.0" }, [...]
$ php composer.phar require lexxpavlov/pagebundle
Source: https://habr.com/ru/post/248055/
All Articles