Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Commit

Permalink
Collection support
Browse files Browse the repository at this point in the history
- Added CollectionField
- Support for storing collections in PHPCR-ODM
- Added form extension.
  • Loading branch information
dantleech committed Jul 27, 2016
1 parent 591bc21 commit 46726f5
Show file tree
Hide file tree
Showing 33 changed files with 1,227 additions and 318 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
],
"minimum-stability": "dev",
"require": {
"symfony/form": "^2.8|^3.0",
"symfony/form": "^3.0@stable",
"jms/metadata": "^1.5",
"symfony/options-resolver": "^2.8|^3.1",
"symfony/options-resolver": "^3.0",
"sylius/registry": "^0.19.0"
},
"require-dev": {
Expand Down
30 changes: 16 additions & 14 deletions docs/introduction.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
CMF Content Type Component
==========================

.. note::

This component does not exist, this is just an idea.

What is it?
-----------

The ContentType component provides a way of mapping content to entities /
documents and providing the infrastructure to display the content in the
frontend and manage the content in the backend.
The ContentType component allows you to map content fields to an object.
Content fields can be simple scalar values, such as text, or compound objects
such as images or geolocations.

It provides:

- **Forms**: How the content type is modified.
- **Storage**: How the content type is stored.
- **View**: How the content type is rendered.
- **Asset Dependencies**: How the content type declares its asset
requirements.

For example, a geolocation field will require at least 3 properties to be
mapped (long, lat, zoom), and we can imagine that it requires some javascript
to show the map in the frontend and also in the backend.

This package handles all of these concerns, namely: (Symfony) Form generation,
Asset depenecies, rendering the frontend content and storage.

In brief this will allow you to define something like:

.. code-block:: yaml
Expand Down Expand Up @@ -88,7 +89,6 @@ Now we can define a content type for it:
type: Symfony\Cmf\Component\ContentType\Field\ResourceList
options:
glob: "/cms/content/news/**/*"
view_options:
page_size: 10
driver: doctrine/phpcr
Expand Down Expand Up @@ -117,12 +117,13 @@ Now you want to add a form to your backoffice, this as simple asking the
<?php
// create the content object (this is just a plain PHP object)
// TODO: What about value objects?
$content = new NewsPage();
// get the form
$form = $contentFormBuilder->buildFormForContent($content);
$formFactory = // get the symfony form factory
$form = $formFactory->create(NewsPage::class);
// submit the data (bypassing validation etc..)
$form->submit($data);
Expand All @@ -132,7 +133,8 @@ Now you want to add a form to your backoffice, this as simple asking the
$entityManager->persist($newsList);
$entityManager->flush();
And it is as simple as that.
And it is as simple as that. Note that we pass the content type class to the
form factory and **NOT** a form type class.

Frontend (website) Rendering
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
6 changes: 3 additions & 3 deletions src/ContentViewBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

use Metadata\MetadataFactory;
use Metadata\NullMetadata;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Cmf\Component\ContentType\OptionsResolver\FieldOptionsResolver;

class ContentViewBuilder
{
Expand Down Expand Up @@ -50,9 +50,9 @@ public function build($content)
$field = $this->fieldRegistry->get($propertyMetadata->getType());
$view = $this->viewRegistry->get($field->getViewType());

$resolver = new OptionsResolver();
$resolver = new FieldOptionsResolver();
$field->configureOptions($resolver);
$options = $resolver->resolve($propertyMetadata->getOptions());
$options = $resolver->resolveViewOptions($propertyMetadata->getOptions());
$value = $propertyMetadata->getValue($content);
$view->buildView($this, $subView, $value, $options);

Expand Down
43 changes: 43 additions & 0 deletions src/Field/CollectionField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2016 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Component\ContentType\Field;

use Symfony\Cmf\Component\ContentType\FieldInterface;
use Symfony\Cmf\Component\ContentType\Form\Extension\Type\FieldCollectionType;
use Symfony\Cmf\Component\ContentType\MappingBuilder;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CollectionField implements FieldInterface
{
public function getViewType()
{
return CollectionView::class;
}

public function getFormType()
{
return FieldCollectionType::class;
}

public function getMapping(MappingBuilder $builder)
{
return $builder->collection();
}

public function configureOptions(OptionsResolver $options)
{
$options->setRequired([
'entry_type',
'allow_add',
]);
}
}
92 changes: 92 additions & 0 deletions src/Form/Extension/FieldExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2016 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Component\ContentType\Form\Extension;

use Metadata\MetadataFactoryInterface;
use Symfony\Cmf\Component\ContentType\FieldRegistry;
use Symfony\Cmf\Component\ContentType\Form\Extension\Type\FieldCollectionType;
use Symfony\Cmf\Component\ContentType\Form\Extension\Type\SurrogateType;
use Symfony\Component\Form\AbstractExtension;

/**
* Form type extension to provide form types for user content-type-managed
* classes in addition to content-type component specific types.
*/
class FieldExtension extends AbstractExtension
{
/**
* @var MetadataFactoryInterface
*/
private $metadataFactory;

/**
* @var FieldRegistry
*/
private $fieldRegistry;

public function __construct(
MetadataFactoryInterface $metadataFactory,
FieldRegistry $fieldRegistry
) {
$this->metadataFactory = $metadataFactory;
$this->fieldRegistry = $fieldRegistry;
}

/**
* {@inheritdoc}
*/
public function hasType($type)
{
if ($this->metadataFactory->getMetadataForClass($type)) {
return true;
}

return parent::hasType($type);
}

/**
* {@inheritdoc}
*/
public function loadTypes()
{
return [
new FieldCollectionType($this->fieldRegistry),
];
}

/**
* {@inheritdoc}
*/
public function getType($type)
{
if (parent::hasType($type)) {
return parent::getType($type);
}

$metadata = $this->metadataFactory->getMetadataForClass($type);

if (!$metadata) {
throw new \InvalidArgumentException(sprintf(
'The type "%s" cannot be loaded by this extension',
$type
));
}

$surrogteType = new SurrogateType(
$type,
$this->fieldRegistry,
$metadata
);

return $surrogteType;
}
}
43 changes: 43 additions & 0 deletions src/Form/Extension/Type/FieldCollectionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2016 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Component\ContentType\Form\Extension\Type;

use Symfony\Cmf\Component\ContentType\FieldRegistry;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FieldCollectionType extends AbstractType
{
private $registry;

public function __construct(FieldRegistry $registry)
{
$this->registry = $registry;
}

public function configureOptions(OptionsResolver $options)
{
$options->setNormalizer('entry_type', function (Options $options, $value) {
// get the field type
$field = $this->registry->get($value);

return $field->getFormType();
});
}

public function getParent()
{
return CollectionType::class;
}
}
82 changes: 82 additions & 0 deletions src/Form/Extension/Type/SurrogateType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2016 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Component\ContentType\Form\Extension\Type;

use Symfony\Cmf\Component\ContentType\FieldRegistry;
use Symfony\Cmf\Component\ContentType\Metadata\ClassMetadata;
use Symfony\Cmf\Component\ContentType\OptionsResolver\FieldOptionsResolver;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* Surrogate type for virtual user "content" form types.
*
* For example, if the user manages an "Article" class with the content type
* system, this class will act as its form type.
*/
class SurrogateType extends AbstractType
{
/**
* @var string
*/
private $contentFqn;

/**
* @var ClassMetadata
*/
private $classMetadata;

/**
* @var FieldRegistry
*/
private $fieldRegistry;

public function __construct(
$contentFqn,
FieldRegistry $fieldRegistry,
ClassMetadata $classMetadata
) {
$this->contentFqn = $contentFqn;
$this->fieldRegistry = $fieldRegistry;
$this->classMetadata = $classMetadata;
}

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($this->classMetadata->getPropertyMetadata() as $propertyMetadata) {
$field = $this->fieldRegistry->get($propertyMetadata->getType());
$formOptions = $propertyMetadata->getOptions();

$resolver = new FieldOptionsResolver();
$field->configureOptions($resolver);
$formOptions = $resolver->resolveFormOptions($formOptions);

$builder->add(
$propertyMetadata->getName(),
$field->getFormType(),
$formOptions
);
}
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', $this->contentFqn);
}
}
Loading

0 comments on commit 46726f5

Please sign in to comment.