I am trying to develop a custom extension using TYPO3 extbase repository method. I want to fetch tt_content "pi_flexform" field value in my extension.
For achieving this I've followed persistence method.
In typoscript configuration files I've added like this;
plugin.tx_myext{
persistence {
classes {
\TYPO3\MyExt\Domain\Model\Myext {
mapping {
tableName = tt_content
recordType = \TYPO3\MyExt\Domain\Model\Myext
columns {
pi_flexform.mapOnProperty = piflexform
}
}
}
}
}
}
In my model file I've implemented the following code;
/**
* #var string
*/
protected $piflexform;
/**
* #return the $piflexform
*/
public function getPiflexform()
{
return $this->piflexform;
}
/**
* #param string $piflexform
*/
public function setPiflexform($piflexform)
{
$this->piflexform = $piflexform;
}
And also in the repository file;
$queryStmt = "SELECT pi_flexform FROM tt_content WHERE CType = 'dce_dce10' AND pid = 1 AND sys_language_uid = 0";
$query = $this->createQuery();
$query->statement( $queryStmt );
return $query->execute();
But it returns an empty object.
TYPO3\CMS\Extbase\Persistence\Generic\QueryResultprototypeobject (empty)
0 => TYPO3\VstRssfeed\Domain\Model\Vstrssfeedprototypepersistent entity (uid=0, pid=0)
piflexform => NULL
uid => 0 (integer)
_localizedUid => 0 (integer)modified
_languageUid => 0 (integer)modified
_versionedUid => 0 (integer)modified
pid => 0 (integer)
Can you guys please help me to solve this problem? Thanks in advance.
How to Effectively use the Repository and Query Object of Extbase?
http://blog.typoplanet.de/2010/01/27/the-repository-and-query-object-of-extbase/
Here's an example how your repository should look like:
myext/Classes/Domain/Repository/ContentRepository.php
<?php
namespace TYPO3\MyExt\Domain\Repository;
/**
* Class ContentRepository
* #package TYPO3\MyExt\Domain\Repository
*/
class ContentRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
/**
* Initializes the repository.
*
* #return void
*/
public function initializeObject() {
/** #var $querySettings \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings */
$querySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($querySettings);
}
/**
* Find By CType
*
* #param string $cType
* #param integer $pid
* #return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findByCtype($cType, $pid = FALSE) {
$constraints = array();
$query = $this->createQuery();
$constraints[] = $query->equals('CType', $cType);
if ($pid) {
$constraints[] = $query->equals('pid', $pid);
}
if ($constraints) {
if (count($constraints) == 1) {
$query->matching($constraints[0]);
} else {
$query->matching($query->logicalAnd($constraints));
}
}
return $query->execute();
}
}
And here's an example how your controller should do the work:
myext/Classes/Controller/SomeController.php
class SomeController extends \TYPO3\CMS\E... {
/**
* #var \TYPO3\MyExt\Domain\Repository\ContentRepository
* #inject
*/
protected $contentRepository = NULL;
public function someAction() {
$contents = $this->contentRepository->findByCtype('dce_dce10');
if ($contents) {
foreach ($contents as $content) {
echo '<h1>'.$content->getUid().'</h1>';
echo PHP_EOL.'<br />';
echo $content->getPiflexform();
echo PHP_EOL.'<br />';
}
}
}
}
Related
I have two entities, MediaObject and Book. MediaObject is a generic entity for managing files and includes fields like size, mimeType, and filePath. Book has fields like title, author, and also includes a link to an associated MediaObject for its cover image file.
How can I POST a Book entity with its associated MediaObject cover image with API-Platform? I'd like to do this as one atomic operation. I don't want books saved without a cover image and I don't want orphan cover images. So I don't want to POST a MediaObject cover image and then use the ID I get back when POSTing a new Book. (or vice-versa)
https://api-platform.com/docs/core/file-upload/
class MediaObject
{
...
public $filePath;
...
}
class Book
{
...
public $coverImage; // i.e. mediaObjectId; associated MediaObject to an image file
...
}
The documentation has the option "deserialize"= false. This means that deserialization will not occur for this operation. Therefore, you must write the entire deserialization process yourself to the handler controller. You must also write fields for the swagger documentation.
For example:
<?php
declare(strict_types=1);
namespace App\Entity;
// more use...
/**
* #ApiResource(
* iri="http://schema.org/MediaObject",
* normalizationContext={
* "groups" = {"media:read"}
* },
* collectionOperations={
* "post" = {
* "controller" = MediaHandler::class,
* "deserialize" = false,
* "access_control" = "is_granted('ROLE_USER')",
* "validation_groups" = {"Default", "media:collection:post"},
* "openapi_context" = {
* "requestBody" = {
* "content" = {
* "multipart/form-data" = {
* "schema" = {
* "type" = "object",
* "properties" = {
* "file" = {
* "type" = "string",
* "format" = "binary"
* },
* "name" = {
* "type" = "string"
* }
* }
* }
* }
* }
* }
* }
* },
* "get"
* },
* itemOperations={
* "get"
* }
* )
* #Vich\Uploadable
* #ORM\Entity(repositoryClass="App\Repository\MediaRepository")
*/
class Media
{
/**
* #ApiProperty(iri="http://schema.org/contentUrl")
* #Groups({"media:read"})
*/
public $contentUrl;
/**
* #Assert\NotNull(groups={"media:collection:post"})
* #Vich\UploadableField(mapping="media", fileNameProperty="filePath")
* #Assert\File(
* maxSize="2M",
* mimeTypes={
* "application/pdf",
* "application/x-pdf",
* "image/jpeg",
* "image/jpg",
* "image/png"
* },
* groups={"media:collection:post"}
* )
*/
public $file;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
*/
private $filePath;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
//...
}
Controller handler example:
<?php
declare(strict_types=1);
namespace App\Controller\Api;
// use ...
class MediaHandler extends AbstractController
{
/**
* #var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function __invoke(Request $request): Media
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$mediaObject = new Media();
$mediaObject->file = $uploadedFile;
$mediaObject->setName($request->request->get('name'));
return $mediaObject;
}
}
If the "Book" exists. And you want to add Book toMediaObject, you can set the iri string and parse it in the controller-handler:
//...
public function __construct(EntityManagerInterface $entityManager, IriConverterInterface $iriConverter)
{
$this->entityManager = $entityManager;
$this->iriConverter = $iriConverter;
}
public function __invoke(Request $request): Media
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$iriBook = $request->request->get('book');
$book = null;
if ($iriBook) {
/**
* #var Book $book
*/
$book = $this->iriConverter->getItemFromIri($iriBook);
}
$mediaObject = new Media();
$mediaObject->file = $uploadedFile;
$mediaObject->setBook($book);
return $mediaObject;
}
//..
If this is your case, then no further action (DataPersist) is required.
Next your need to go https://api-platform.com/docs/core/data-persisters/ and make DataPesist handler
Example:
<?php
declare(strict_types=1);
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Media;
use App\ExtendTrait\ContextAwareDataTrait;
use Doctrine\ORM\EntityManagerInterface;
class MediaObjectDataPersister implements ContextAwareDataPersisterInterface
{
use ContextAwareDataTrait;
/**
* #var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* {#inheritdoc}
*/
public function supports($data, array $context = []): bool
{
return $this->isCollection('post', $context) && $data instanceof Media;
}
/**
* {#inheritdoc}
*
* #param $data Media
*
* #throws \Exception
*/
public function persist($data, array $context = []): void
{
$book = new Book();
$book->setName($data->getName());
// begin transaction and persist and flush $book and $data
}
/**
* {#inheritdoc}
*/
public function remove($data, array $context = []): void
{
// todo remove book
}
}
P.S. I don't test this code. I writing idea ;)
P.S.S. $this->isCollection() it function from my trait, may be need you it:
<?php
declare(strict_types=1);
namespace App\ExtendTrait;
/**
* Trait ContextAwareDataTrait.
*
* Helps confirm the operation name
*/
trait ContextAwareDataTrait
{
public function isItem(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['item_operation_name'] ?? null) === $operationName;
}
public function isCollection(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['collection_operation_name'] ?? null) === $operationName;
}
}
I created a custom widget viewhelper for an extension which works fine in an empty typo3 8.7 installation. But when I use it on needed project with same code it causes an error:
#1289422564: initiateSubRequest() can not be called if there is no valid controller extending TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController Got "NULL" in class ...
Did somebody have an error like this before or does someone know what causes this type of error?
<!-- This is the View List.html-->
{namespace Exhibitors = MyVendorName\MyExhibitors\ViewHelpers}
<ul class="second_lvl">
<Exhibitors:widget.AtoZNav objects="{feUserData}" as="filteredExhibitors" property="company">
<Exhibitors:widget.sort objects="{filteredExhibitors}" as="sortedExhibitors" property="company">
<f:for each="{filteredExhibitors}" as="feUser">
<li class="navLink">
{feUser.company}<br />
{feUser.company}
{feUser.www}<br />
</li>
</f:for>
</Exhibitors:widget.sort>
</Exhibitors:widget.AtoZNav>
</ul>
<f:link.action action="show">show detail page</f:link.action>
<?php
/**
* This is the SortViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class SortViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
<?php
/**
* This is the Sort Controller
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget\Controller;
class SortController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* #var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
protected $objects;
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
}
/**
* #param mixed string $order
*/
public function indexAction($order = \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING)
{
$order = ($order == \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING) ? \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING : \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING;
$query = $this->objects->getQuery();
$query->setOrderings(array($this->widgetConfiguration['property'] => $order));
$modifiedObjects = $query->execute();
$this->view->assign('contentArguments', array($this->widgetConfiguration['as'] => $modifiedObjects));
$this->view->assign('order', $order);
}
}
<?php
/**
* This is AtoZNav ViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class AtoZNavViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
<?php
/**
* This is the Controller
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget\Controller;
class AtoZNavController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* #var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
protected $objects;
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
}
/**
* #param string $char
* #throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
*/
public function indexAction($char = '%')
{
$query = $this->objects->getQuery();
$query->matching($query->like($this->widgetConfiguration['property'], $char . '%'));
$modifiedObjects = $query->execute();
$this->view->assign('contentArguments', array($this->widgetConfiguration['as'] => $modifiedObjects));
$this->view->assign('letters', range('A', 'Z'));
$this->view->assign('char', $char);
}
}
I got the same error. You need to inject your controller, like it is done in the fluid pagination widget. This part is missing in the book.
<?php
/**
* This is the SortViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class SortViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
protected $controller;
/**
* #param Controller/SortController $controller
*/
public function injectSortController(Controller\SortController $controller)
{
$this->controller = $controller;
}
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
How to unit test Symfony 4 Form with EntityType field
When I run my test:
$ ./vendor/bin/simple-phpunit tests/Unit/Form/ProductFormTest.php
This is the output in my terminal:
PHPUnit 6.5.8 by Sebastian Bergmann
and contributors.
Runtime: PHP 7.2.4-1+ubuntu16.04.1+deb.sury.org+1 with Xdebug
2.7.0alpha2-dev Configuration: /var/www/project/phpunit.xml.dist
Testing App\Tests\Unit\Form\ProductFormTest E
1 / 1 (100%)
Time: 551 ms, Memory: 6.00MB
There was 1 error:
1) App\Tests\Unit\Form\ProductFormTest::formSubmitsValidData
Symfony\Component\Form\Exception\RuntimeException: Class
"App\Entity\Supplier" seems not to be a managed Doctrine entity. Did
you forget to map it?
/var/www/project/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:205
/var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:858
/var/www/project/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:130
/var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:766
/var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:698
/var/www/project/vendor/symfony/form/ResolvedFormType.php:95
/var/www/project/vendor/symfony/form/FormFactory.php:76
/var/www/project/vendor/symfony/form/FormBuilder.php:97
/var/www/project/vendor/symfony/form/FormBuilder.php:256
/var/www/project/vendor/symfony/form/FormBuilder.php:206
/var/www/project/vendor/symfony/form/FormFactory.php:30
/var/www/project/tests/Unit/Form/ProductFormTest.php:86
ERRORS! Tests: 1, Assertions: 0, Errors: 1.
This error started after mocking the ManagerRegistry class. It seems that in this unit test there is no mapping for doctrine entities present.
Is there a clean way to test a form with "Symfony\Bridge\Doctrine\Form\Type\EntityType" fields?
src\App\Entity\Product.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Supplier;
/**
* Product Entity
*
* #ORM\Entity(repositoryClass = "App\Repository\ProductRepository")
* #ORM\Table(name = "product")
*/
class Product
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->setType(AbstractProduct::TYPE_PARENT);
}
/**
* To String
*
* #return string
*/
public function __toString()
{
return "[" . $this->id . "] Product: " . $this->ean . " | " . $this->name;
}
/**
* ID
*
* #var integer
*
* #ORM\Id
* #ORM\Column(name = "product_id", type = "integer")
* #ORM\GeneratedValue(strategy = "AUTO")
*/
protected $id;
/**
* EAN (European Article Number)
*
* #var string
*
* #ORM\Column(name = "product_ean", type = "string", length = 13)
*/
protected $ean;
/**
* Name
*
* #var string
*
* #ORM\Column(name = "product_name", type = "string", length = 128)
*/
protected $name;
/**
* Description
*
* #var string
*
* #ORM\Column(name = "product_description", type = "text", nullable = true)
*/
protected $description;
/**
* Supplier
*
* Many Products have one Supplier
*
* #var Supplier
*
* #ORM\ManyToOne(targetEntity = "Supplier", inversedBy = "products")
* #ORM\JoinColumn(name = "supplier_id", referencedColumnName = "supplier_id")
*/
protected $supplier;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set ean
*
* #param string $ean
*
* #return AbstractProduct
*/
public function setEan($ean)
{
$this->ean = $ean;
return $this;
}
/**
* Get ean
*
* #return string
*/
public function getEan()
{
return $this->ean;
}
/**
* Set name
*
* #param string $name
*
* #return AbstractProduct
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
*
* #return AbstractProduct
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set supplier
*
* #param \App\Entity\Supplier $supplier
*
* #return Product
*/
public function setSupplier(Supplier $supplier = null)
{
$this->supplier = $supplier;
return $this;
}
/**
* Get supplier
*
* #return \App\Entity\Supplier
*/
public function getSupplier()
{
return $this->supplier;
}
}
src\App\Form\ProductForm.php
namespace App\Form;
use App\Entity\Supplier;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class ProductForm extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$translationDomain = "product";
/*
* Card
*/
$builder->add("ean", TextType::class, [
"label" => "product.ean",
"required" => true,
"translation_domain" => $translationDomain,
]);
$builder->add("name", TextType::class, [
"label" => "product.name",
"required" => true,
"translation_domain" => $translationDomain,
]);
$builder->add("supplier", EntityType::class, [
"class" => Supplier::class,
"choice_label" => "name",
"label" => "supplier.name",
"required" => false,
"translation_domain" => "supplier",
]);
}
}
tests\Unit\Form\ProductFormTest.php
namespace App\Tests\Unit\Form;
use App\Entity\Product;
use App\Form\ProductForm;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class ProductFormTest extends TypeTestCase
{
/**
* #var ManagerRegistry
*/
private $_managerRegistry;
/**
* {#inheritdoc}
*/
protected function setUp()
{
$this->_managerRegistry = $this->createMock(ManagerRegistry::class);
parent::setUp();
}
/**
* {#inheritdoc}
*/
protected function tearDown()
{
$this->_managerRegistry = null;
parent::tearDown();
}
/**
* {#inheritdoc}
*/
protected function getExtensions()
{
$entityType = new EntityType($this->_managerRegistry);
return [
new PreloadedExtension([$entityType], [])
];
}
/**
* #test
*/
public function formSubmitsValidData()
{
$createdAt = new \DateTime();
$formData = [
"ean" => "8718923400440",
"name" => "Plumbus",
"description" => "This is a household device so common it does not need an introduction",
];
$productComparedToForm = new Product();
$productComparedToForm
->setEan($formData["ean"])
->setName($formData["name"])
;
$productHandledByForm = new Product();
$form = $this->factory->create(ProductForm::class, $productHandledByForm);
$form->submit($formData);
static::assertTrue($form->isSynchronized());
static::assertEquals($productComparedToForm, $productHandledByForm);
$view = $form->createView();
foreach (array_keys($formData) as $key) {
static::assertArrayHasKey($key, $view->children);
}
}
}
First your test case should extends from Symfony\Component\Form\Test\TypeTestCase.
Then your test should look like this:
// Example heavily inspired by EntityTypeTest inside the Symfony Bridge
class ProductTypeTest extends TypeTestCase
{
/**
* #var EntityManager
*/
private $em;
/**
* #var \PHPUnit_Framework_MockObject_MockObject|ManagerRegistry
*/
private $emRegistry;
protected function setUp()
{
$this->em = DoctrineTestHelper::createTestEntityManager();
$this->emRegistry = $this->createRegistryMock('default', $this->em);
parent::setUp();
$schemaTool = new SchemaTool($this->em);
// This is the important part for you !
$classes = [$this->em->getClassMetadata(Supplier::class)];
try {
$schemaTool->dropSchema($classes);
} catch (\Exception $e) {
}
try {
$schemaTool->createSchema($classes);
} catch (\Exception $e) {
}
}
protected function createRegistryMock($name, $em)
{
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock();
$registry->expects($this->any())
->method('getManager')
->with($this->equalTo($name))
->will($this->returnValue($em));
return $registry;
}
protected function getExtensions()
{
return array_merge(parent::getExtensions(), array(
new DoctrineOrmExtension($this->emRegistry),
));
}
protected function tearDown()
{
parent::tearDown();
$this->em = null;
$this->emRegistry = null;
}
}
another solution if the one above does not work. Example on another application.
namespace App\Tests\Form;
use App\Entity\BusinessDepartment;
use App\Entity\Contact;
use App\Form\ContactForm;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class ContactFormTest extends TypeTestCase
{
protected function getExtensions() {
$mockEntityManager = $this->createMock(EntityManager::class);
$mockEntityManager->method('getClassMetadata')
->willReturn(new ClassMetadata(BusinessDepartment::class))
;
$execute = $this->createMock(AbstractQuery::class);
$execute->method('execute')
->willReturn([]);
$query = $this->createMock(QueryBuilder::class);
$query->method('getQuery')
->willReturn($execute);
$entityRepository = $this->createMock(EntityRepository::class);
$entityRepository->method('createQueryBuilder')
->willReturn($query)
;
$mockEntityManager->method('getRepository')->willReturn($entityRepository);
$mockRegistry = $this->createMock(ManagerRegistry::class);
$mockRegistry->method('getManagerForClass')
->willReturn($mockEntityManager)
;
return array_merge(parent::getExtensions(), [new DoctrineOrmExtension($mockRegistry)]);
}
public function testBuildForm()
{
$data = [
'name' => 'nameTest',
'firstName' => 'firstnameTest',
'email' => 'test_email#gmail.com',
'message' => 'messageTest'
];
$contact = new Contact();
$form = $this->factory->create( ContactForm::class, $contact);
$contactToCompare = new Contact();
$contactToCompare->setName($data['name']);
$contactToCompare->setFirstName($data['firstName']);
$contactToCompare->setEmail($data['email']);
$contactToCompare->setMessage($data['message']);
//check the submission
$form->submit($data);
$this->assertTrue($form->isSynchronized());
$this->assertEquals($contact->getName(), $contactToCompare->getName());
$this->assertEquals($contact->getFirstName(), $contactToCompare->getFirstName());
$this->assertEquals($contact->getEmail(), $contactToCompare->getEmail());
$this->assertEquals($contact->getMessage(), $contactToCompare->getMessage());
}
}
another solution if the one above does not work. Example on another application.
ContactFromTest.php
namespace App\Tests\Form;
use App\Entity\BusinessDepartment;
use App\Entity\Contact;
use App\Form\ContactForm;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class ContactFormTest extends TypeTestCase
{
protected function getExtensions() {
$mockEntityManager = $this->createMock(EntityManager::class);
$mockEntityManager->method('getClassMetadata')
->willReturn(new ClassMetadata(BusinessDepartment::class))
;
$execute = $this->createMock(AbstractQuery::class);
$execute->method('execute')
->willReturn([]);
$query = $this->createMock(QueryBuilder::class);
$query->method('getQuery')
->willReturn($execute);
$entityRepository = $this->createMock(EntityRepository::class);
$entityRepository->method('createQueryBuilder')
->willReturn($query)
;
$mockEntityManager->method('getRepository')->willReturn($entityRepository);
$mockRegistry = $this->createMock(ManagerRegistry::class);
$mockRegistry->method('getManagerForClass')
->willReturn($mockEntityManager)
;
return array_merge(parent::getExtensions(), [new DoctrineOrmExtension($mockRegistry)]);
}
public function testBuildForm()
{
$data = [
'name' => 'nameTest',
'firstName' => 'firstnameTest',
'email' => 'test_email#gmail.com',
'message' => 'messageTest'
];
$contact = new Contact();
$form = $this->factory->create( ContactForm::class, $contact);
$contactToCompare = new Contact();
$contactToCompare->setName($data['name']);
$contactToCompare->setFirstName($data['firstName']);
$contactToCompare->setEmail($data['email']);
$contactToCompare->setMessage($data['message']);
//check the submission
$form->submit($data);
$this->assertTrue($form->isSynchronized());
$this->assertEquals($contact->getName(), $contactToCompare->getName());
$this->assertEquals($contact->getFirstName(), $contactToCompare->getFirstName());
$this->assertEquals($contact->getEmail(), $contactToCompare->getEmail());
$this->assertEquals($contact->getMessage(), $contactToCompare->getMessage());
}
}
ContacForm.php
class ContactForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',TextType::class, [
'label' => 'Nom'
])
->add('firstName',TextType::class, [
'label' => 'Prénom'
])
->add('email', EmailType::class, [
'label' => 'Email'
])
->add('businessDepartment', EntityType::class, [
'label' => 'Département à contacter',
'class' => BusinessDepartment::class,
'choice_value' => 'id',
'choice_label' => 'nameDepartment',
])
->add('message', TextareaType::class, [
'label' => 'Votre message'
])
;
}
}
I'm creating a content Element with gridelments where i need to render some Data from the childs directly. There is no problem for fields like "bodytext" or "header". But the assets gives me only a counter, not a reference or path.
So the big question: How can i render the assets images from the childs?
I can share a VH I just did
<?php
namespace GeorgRinger\Theme\ViewHelpers;
use TYPO3\CMS\Core\Database\DatabaseConnection;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Frontend\Resource\FileCollector;
class FalViewHelper extends AbstractViewHelper
{
/**
* #var boolean
*/
protected $escapeOutput = FALSE;
/**
* #param string $table
* #param string $field
* #param string $id
* #param string $as
* #return string
*/
public function render($table, $field, $id, $as = 'references')
{
$row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', $table, 'uid=' . (int)$id);
if (!$row) {
return '';
}
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
$fileCollector->addFilesFromRelation($table, $field, $row);
$this->templateVariableContainer->add($as, $fileCollector->getFiles());
$output = $this->renderChildren();
$this->templateVariableContainer->remove($as);
return $output;
}
/**
* #return DatabaseConnection
*/
protected function getDatabaseConnection()
{
return $GLOBALS['TYPO3_DB'];
}
}
Of course you need to adopt the namspace.
Usage would be
<theme:fal table="tt_content" field="assets" id=" => the id <= ">
<f:debug>{references}</f:debug>
</theme:fal>
Simple one hopefully, is there a specific way i should be updating a single database value using a model in Zend Framework.
I currently do this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
$_name = 'table';
public function updateSetting($id,$status)
{
$data = array(
'status' => $status
);
$this->update($data, 'id = '.$id);
}
}
$update = new Model_MyModel();
$update->updateSetting(10,1);
Obviously i could pass in another argument as the column to update. I just wondered if there was a more "magic" way i should be doing this?
You could write a simple property overloader for this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
protected $_name = 'table';
/**
* Should be a Zend_Db_Table_Row instance
*
* #var Zend_Db_Table_Row
*/
protected $_currentRow = null;
/**
* Property overloader
*
* For more information on this see
* http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members
*
* #param string $key
* #param string $value
* #return void
*/
public function __set($key, $value)
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->$key = $value;
}
else
{
throw new Exception('Cannot update a column on a non existent row!');
}
}
/**
* Save current row
*
* #return Model_MyModel
*/
public function saveCurrentRow()
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->save();
}
else
{
throw new Exception('Cannot save a non existent row!');
}
}
/**
* Set current row
*
* #param Zend_Db_Table_Row $row
* #return Model_MyModel
*/
public function setCurrentRow(Zend_Db_Table_Row $row)
{
$this->_currentRow = $row;
return $this;
}
/**
* Get current row
*
* #return Zend_Db_Table_Row
*/
public function getCurrentRow()
{
return $this->_currentRow;
}
}
You could then do stuff like this:
$model = new Model_MyModel();
$model->status = 'foo';
$model->somecolumn = 'bar'
$model->saveCurrentRow();
Although this approach would require the least editing in your code, an even better approach would be seperating your models from your database tables and use a Data Mapper Pattern as described in the Quickstart.