How to realize inheritance in Typo3 6.2 Extension? - typo3

My goal is being able to:
Create Expertise Entries in the backend (already accomplished)
Create SubExpertise Entries in the backend
(same props as Expertise but
they belong to one or many Expertise)
Create AdditionalInfoTitles Entries in the backend
(they can belong to one or many Expertise OR SubExpertise)
I want to be able to choose Objects from all Expertise AND SubExpertise when creating a new entry
Right now I can only choose between all Expertise-Entries:
That's why I thought about inheritance since then SubExpertise would be of the same type as Expertise and therefore automatically displayed in the Expertise list in a AdditionalInfoTitles entry. But that's just my theory and I'm kinda stuck in reality with typo3 TCA and other knowledge that I'm lacking...
In my extension builder I made following (don't mind the subExpertises property)
Then I added expertise to the Overrides folder, because I'm trying to extend it with subexpertise:
<?php
if (!defined('TYPO3_MODE')) {
die ('Access denied.');
}
$temporaryColumns = array (
'expertise' => array(
'exclude' => 1,
'label' => 'LLL:EXT:appoints/Resources/Private/Language/locallang_db.xlf:tx_appoints_domain_model_subexpertise.expertise',
'config' => array(
'type' => 'select',
'foreign_table' => 'tx_appoints_domain_model_subexpertise',
'MM' => 'tx_appoints_subexpertise_expertise_mm',
'size' => 10,
'autoSizeMax' => 30,
'maxitems' => 9999,
'multiple' => 0,
'wizards' => array(
'_PADDING' => 1,
'_VERTICAL' => 1,
'edit' => array(
'module' => array(
'name' => 'wizard_edit',
),
'type' => 'popup',
'title' => 'Edit',
'icon' => 'edit2.gif',
'popup_onlyOpenIfSelected' => 1,
'JSopenParams' => 'height=350,width=580,status=0,menubar=0,scrollbars=1',
),
'add' => Array(
'module' => array(
'name' => 'wizard_add',
),
'type' => 'script',
'title' => 'Create new',
'icon' => 'add.gif',
'params' => array(
'table' => 'tx_appoints_domain_model_expertise',
'pid' => '###CURRENT_PID###',
'setValue' => 'prepend'
),
),
),
),
),
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
'tx_appoints_domain_model_expertise',
$temporaryColumns
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
'tx_appoints_domain_model_expertise',
'expertise'
);
But I don't think I'm going into the right direction with this -
Because I think this way I'm not gonna be able to add a SubExpertise in the backend separately from an Expertise - I already have the same problem with my Objects that extend fe_user because when creating them I usually have to go through a new User and then set the extension type - but this way I don't have separate listings of the different entities that extend fe_user.

I would get rid of the separation between Expertise and SubExpertise for the most part. According to your description a SubExpertise cannot have another SubExpertise as its parent, so you can adapt the select field that it only lists Expertises which have an empty parent field.
By removing the difference the problem of selecting (Sub)Expertise's in AdditionalInfoTitles is removed; it's just one and the same type of objects.
If you need to differentiate in the presentation in the BE forms there are plenty of options to adjust the labels of the listed items, use a function of your own to build the list or even a custom form element.
In Extbase you can simply write a few functions in your repository to fetch Expertise's, SubExpertise's or both.

If the entity SubExpertise does not have a meaning in your domain model, Jigal's answer is perfect for your scenario. If it does have a meaning, you can achieve that using single table inheritance in Extbase.
class Expertise extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
// all common properties
}
class SubExpertise extends Expertise
{
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\[YourVendorName]\Appoints\Domain\Model\Expertise>
*/
protected $expertises;
public function __construct()
{
$this->expertises = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
public function getExpertises() {}
public function setExpertises($expertises) {}
}
Via TypoScript then you have to define mapping rules, since both Expertise and SubExpertise would be stored in the same table tx_appoints_domain_model_subexpertise.
You'll find more details on single table inheritance in the Extbase book.

Related

Extend TYPO3 News Repository with M:M Relation

I've been trying to extend tx_news with a m:m relation with not much luck so far. Anything I can find online is just for a regular relation, where the ID is saved directly into the same table, not an extra _mm column.
The backend looks fine so far, the way I want it with a selectMultipleSideBySide renderType. It also saves the relations to the database.
Extending the News TCA with this, works fine:
$extendArtistId = array(
'artist_id' => array (
'exclude' => 0,
'l10n_mode' => 'exclude',
'label' => 'Künstler',
'config' => array(
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => TRUE,
'foreign_table' => 'tx_bfartistmanagement_domain_model_kuenstler',
'foreign_table_where' => 'AND tx_bfartistmanagement_domain_model_kuenstler.sys_language_uid = 0 AND tx_bfartistmanagement_domain_model_kuenstler.pid = 63 ORDER BY tx_bfartistmanagement_domain_model_kuenstler.name ASC',
'MM' => 'tx_news_domain_model_news_artist_id_mm',
'minitems' => 0,
'maxitems' => 99
),
),
);
And the entries are saved the way I expected:
The problem I'm having now is getting these relations from a different extension. News has something called "Hooks", but I'm not sure I want/need that for my case.
The query inside my other extension currently looks like this, newsRepository being injected:
$news = $this->newsRepository->findByArtistId(intval($userID));
I struggle with the next step(s). The function findByArtistId was working before as I was saving the artistId directly inside the news table, but that only works with a direct relation, not an M:M one.
How can I get the News that are associated with that artist, with an m:m relation?

TYPO3 Extbase TCA MM-relation filter results by site-defined constant "newRecordStoragePid"

TLDR:
I've got two models with MM-Relations with different StoragePids defined via Constants in my template.
I don't know how to filter results while querying my data regarding my configured storagePids for my related Model.
Long version:
In my multisite-TYPO3 installation I've got two models "Person" and "PersonalInformation". These models have a MM-Relation defined via TCA.
"Person" contains all general data, stored in a global RecordStore. "PersonalInformation" contains editable Data i.e. images to be editable for each site separately. These data are stored in seperate RecordStores under each site.
That means within each site-template->Constants I've defined the extension-storagePid i.e.: $plugin.tx_myext.persistence.storagePid = 1
This config is on all sites the same, to be able to access the same RecordStore from each Site.
The RecordStore for "PersonalInformation" should be different for each site. So my setup.txt of my extension looks like:
persistence {
storagePid = {$plugin.tx_myext.persistence.storagePid},
{$plugin.tx_tx_myext.persistence.personalInformationStoragePid}
classes {
TYPO3\T3myext\Domain\Model\PersonalInformation {
newRecordStoragePid = {$plugin.tx_myext.persistence.personalInformationStoragePid}
}
}
}
And in my root-site-template under Constants I've defined plugin.tx_myext.persistence.personalInformationStoragePid for each site individually.
My TCA MM-Relation defined for PersonalInformation:
'person' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_person',
'config' => array(
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_myext_domain_model_person',
'foreign_table_where' => 'AND 1=1 ORDER BY last_name ASC',
'MM' => 'tx_myext_person_personalinformation_mm',
'size' => 10,
'autoSizeMax' => 30,
'maxitems' => 1,
'minitems' => 0,
'multiple' => 0,
),
),
My TCA MM-Relation defined for Person:
'personalinformation' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_person.personalinformation',
'config' => array(
'type' => 'none',
'readonly' => 1,
'foreign_table' => 'tx_myext_domain_model_personalinformation',
'MM_opposite_field' => 'personalinformation',
'MM' => 'tx_myext_person_personalinformation_mm',
'foreign_table_where' => 'AND tx_myext_domain_model_personalinformation.pid=###The-PID-defined-in-my-site-Const-for-personalInformationStoragePid###'
),
),
If I var_dump my Person in the Frontend all Person.PersonaInformation of all RecordStores are displayed. But I what to show only PersonalInformation Records of the current Site.
The field in the model will always give back all relations, independent of the storage pid. The foreign_table_where in TCA is only for the backend, so this will do nothing for the frontend.
If you want to only get relations from a certain pid, there are several solutions:
Filter it yourself, either in your template, model or controller. Just loop through the relations and check the pid. This option is easiest, but will be slow if you have a lot of relations.
Select the PersonalInformation records separately in your controller using a PersonalInformationRepository with a findByPerson function. This will respect the storagePid set in TypoScript. This will work fine if you only need the information for 1 person. If you need it for multiple persons on 1 page (in a list view for example) you can do this in a custom getPersonalInformation function in your Person model. If it's not cached it could also be slow for lists (depending on the amount of records).
Use a completely custom query using QueryBuilder (https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html). This way you can do it in 1 query with joins.
What is the best solution depends on your exact situation and the number of records.

Symfony 2.3 add form collections of entities

I'm triying to make a dynamic form adding collections inside an entity.
I have followed the code example in the Symfony's documentation, and it works, but what I want to do is add a new form (the form of the entity collections).
So, if I have an entity A that contains a collection of entities B, I want to add new entities B dynamically in the form, but I don't know how to do it.
The entity A form should be something like:
$builder->add('entityB', 'collection', array(
'type' => 'HOW TO PUT THE FORM OF THE ENTITY B???',
'options' => array(
'required' => false,
),
'allow_add'=>true,
));
Taken from the Cookbook:
$builder->add('entityB', 'collection', array(
'type' => new EntityBType(),
'options' => array(
'required' => false
),
'allow_add' => true
));
This is assuming that you have created a Form Type Class for EntityB (not manually creating it when needed in your controller). The linked cookbook entry gives a lot of good examples based on per-case situations.

Cakephp automatically filled form select for two word named belongTo model

What is right naming or what am I missing to get automagic run for two word named Model. Actual model belong to the two words named model.
Exact example:
Tour belongs to Accommodation type.
in database there is table tours and table accommodation_types
foreign key from tours is tours.accommodation_type_id
Snapshots of code below.
ToursController.php
public function add() {
//...
$accommodation_types = $this->Tour->AccommodationType->find('list');
//...
$this->set(compact('accommodation_types', ...));
}
Tour.php
//...
public $belongsTo = array(
//...
'AccommodationType' => array(
'className' => 'AccommodationType',
'foreignKey' => 'accommodation_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//...
);
Tours/add.ctp (inside a form)
echo $this->Form->input('accommodation_type_id', array('label' => 'Accommodation type'));
As per convention the view vars names should be camelBacked. So rename the view var from $accommodation_types to $accommodationTypes. If you don't follow convention you have to explicitly specify the options var to use like this:
echo $this->Form->input('accommodation_type_id', array('options' => $accommodation_types, 'label' => 'Accommodation type'));

Example of Zend Form with Collection Element using Forms not Fieldsets in Zend Framework2

I need a straight forward working example how I can include a collection element in Zend Form, I have seen some examples from Zend Framework 2 site and from previous posts in StackOverflow where most of them pointed to this link. But right now I am not using Fieldsets and staying with Forms, so in case if someone can direct me in the right way, how I can include a simple collection element when the user gets a page where the user can choose multiple choices from the shown collection form. Much better would be populating the collection form from database.
I have searched in the internet for quite a sometime now and thought I would post here, so that Zend profis can give their suggestions.
Just For Information:
Normally one can include a static dropdownbox in Zend Form in this fashion
$this->add(
array(
'name' => "countr",
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => "Countries",
'options' => array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
)
)
)
);
So I am expecting a simple example which could give me a basic idea how this can be done.
To be honest, I don't see your problem here. Since form collections extend Fieldset which extends Element, you can just add it to the form as a regular element. The view helpers will take care of the rendering recursively.
Step 1: Create a form collection (create an instance of Zend\Form\Element\Collection). If the elements have to be added dynamically in some way, I'd create a factory class for this purpose.
Step 2: Add it to the form. (For example using $form->add($myCollectionInstance).)
Step 3: Render it. Zend\Form\View\Helper\Collection is a pretty good view helper to render the whole form without any pain.
You can also create a new class extending Zend\Form\Element\Collection and use the constructor to add the fields you need. Thus, you can add it to the form using the array you've pasted in your question. Also, you could directly use it in annotations.
Hope this helps.
If you just want to fill in a select list with option values you can add the array to the select list in a controller:
$form = new MyForm();
$form->get('countr')->setOptions(array('value_options'=>array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
the array can be fetched from db.
this is a different example for using form collections in the simplest way.
In this example it creates input text elements in a collection and fills them in. The number of elements depends on the array:
class MyForm extends \Zend\Form\Form
{
$this->add(array(
'type' => '\Zend\Form\Element\Collection',
'name' => 'myCollection',
'options' => array(
'label' => 'My collection',
'allow_add' => true,
)
));
}
class IndexController extends AbstractActionController
{
public function indexAction
{
$form = new MyForm();
$this->addElementsFromArray($form, array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
//the above line can be replaced if fetching the array from a db table:
//$arrayFromDb = getArrayFromDb();
//$this->addElementsFromArray($form, $arrayFromDb);
return array(
'form' => $form
);
}
private function addElementsFromArray($form, $array)
{
foreach ($array as $key=>$value)
{
$form->get('myCollection')->add(array(
//'type' => '\Zend\Form\Element\SomeElement',
'name' => $key,
'options' => array(
'label' => $key,
),
'attributes' => array(
'value' => $value,
)
));
}
}
}
index.phtml:
$form->setAttribute('action', $this->url('home'))
->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form->get('myCollection'));
echo $this->form()->closeTag();