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

Collection support #10

Merged
merged 1 commit into from
Aug 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing doc

{
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
));
}

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

return $surrogateType;
}
}
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