My extbase model "show", has got a lot of records "production". A Production can be "ended" or "current". It's declared in the Model of productions.
In a "Show"-Show I want to list the current productions and the ended ones. Currently it's only possible to list it with <f:for each="{productions}"...> and a condition.
But I want to do it like in this way <f:for each="{currentProductions}"... or <f:for each="{endedProductions}"...
So I wrote a query for my repository of productions:
public function aktuelleProduktionen(Musicalplanet\MpDatenbank\Domain\Model\Produktionen $produktionen) {
$query = $this->createQuery();
$query->matching(
$query->logicalOr(
$query->equals('openend', '1'),
$query->equals('abgelaufen', '0')
)
);
return $query->execute();
}
but now I really don't know how to make it working.
For me it's not clear where to add it, to use it like I need it.
First of all, I think your query method should be more like this to get the productions of a show:
public function aktuelleProduktionen(Musicalplanet\MpDatenbank\Domain\Model\Show $show) {
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals('show', $show->getUid()),
$query->logicalOr(
$query->equals('openend', '1'),
$query->equals('abgelaufen', '0')
)
)
);
return $query->execute();
}
I guess, when you have a show-Model, you have a Show-Controller, too. Inside that, you should inject your ProductionRepository:
/**
* programRepository
*
* #var \Musicalplanet\MpDatenbank\Domain\Model\ProduktionenRepository
* #inject
*/
protected $produktionenRepository;
Now you can use your custom query method to get your current productions out of that Repository. Do that in your showAction().
public function showAction(Musicalplanet\MpDatenbank\Domain\Model\Show $show) {
$currentProductions = $this->produktionenRepository->aktuelleProduktionen($show);
$this->view->assign('currentProductions', $currentProductions);
}
More or less, this should work. Hope that helps.
Related
I have a TYPO3 repository function that create a like-query.
I wonder, if I have to sanitize the user input to prevent sql-injection and if so, how.
I read s.th. that this is automatically done by the doctrine layer.
I'm on TYPO3 9.5.
Please advice.
Here is my repository-class together with the function:
class ProductRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
/**
* find
*
* #return Tx_Extbase_Persistence_QueryResult
*/
public function findAllLike( $name) {
$query = $this->createQuery();
$orConstraints = array();
$orConstraints[] = $query->like('productname', '%'.$name.'%');
$orConstraints[] = $query->like('tradename','%'.$name.'%');
$constraints[] = $query->logicalOr($orConstraints);
return $query->matching($query->logicalAnd($constraints))->execute();
}
Yes you have to escape your like query
see the documentation on TYPO3 Querybuilder
https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/Database/QueryBuilder/Index.html#database-query-builder-escape-like-wildcards
I have made a 'document' model which contains a field 'file' which is a FileReference. Now im working on a repository query function that retrieves all documents containing certain string in the files name ( using $query->like() for this ). However I run against the following error:
When ever I disable this $query->like and I debug the 'document' that I receive it looks like the relation information regarding the field 'uidLocal' is correct because I receive the name of the file.
Some more code im using:
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference
{
/**
* #var \**\***\Domain\Model\File
*/
protected $uidLocal;
/**
* #param \**\***\Domain\Model\File $uidLocal
* #return void
*/
public function setUidLocal($uidLocal)
{
$this->uidLocal = $uidLocal;
}
/**
* #return \**\***\Domain\Model\File
*/
public function getUidLocal()
{
return $this->uidLocal;
}
}
The repository query:
$query->matching(
$query->logicalAnd(
$query->greaterThanOrEqual('crdate', $from),
$query->contains('usergroups', $participant),
// TODO: Onderstaande check moet aan maar resulteerd in error..
$query->like('file.uidLocal.name', '%'.$filename.'_'.$type.'.%')
)
);
Now I can ofcourse filter the document name after the query but that wont do well for the performance of the task. Does anyone know what im missing and where the error comes from?
Thanks in advance for contributing ideas,
Falko
I guess you are trying something like that?
$query = $this->createQuery();
$query->matching($query->like('file.uidLocal.name', '%somefile%'));
I am not sure if you can join from sys_file_reference to sys_file with extbase query.
For me it looks like the TCA or the FileReference Model is not implemented that way.
Maybe a workaround is to create a custom sql query wich returns uids and then create your document models with something like ->findAllByUids($uids);
Here is a working example wich uses the query builder instead of extbase query.
class ArticleRepository extends Repository
{
public function findAllByFilename($filename)
{
/* #var $queryBuilder \TYPO3\CMS\Core\Database\Query\QueryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_example_domain_model_article');
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
$res = $queryBuilder
->select('article.uid')
->from('tx_example_domain_model_article', 'article')
->leftJoin('article', 'sys_file_reference', 'reference',
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('reference.uid_foreign', $queryBuilder->quoteIdentifier('article.uid')),
$queryBuilder->expr()->eq('reference.tablenames', $queryBuilder->quote('tx_example_domain_model_article')),
$queryBuilder->expr()->eq('reference.fieldname', $queryBuilder->quote('image')),
$queryBuilder->expr()->eq('reference.table_local', $queryBuilder->quote('sys_file'))
))
->leftJoin('article', 'sys_file', 'file',
$queryBuilder->expr()->eq('file.uid', $queryBuilder->quoteIdentifier('reference.uid_local'))
)
->where($queryBuilder->expr()->eq('file.name', $queryBuilder->createNamedParameter($filename)))
->execute();
debug($queryBuilder->getSQL());
$uids = [];
while ($row = $res->fetch()) {
$uids[] = $row['uid'];
}
return $this->findAllByUids($uids);
}
public function findAllByUids($uids) {
$query = $this->createQuery();
$query->matching($query->in('uid', $uids));
return $query->execute();
}
I want to develop an extension for TYPO3 6.2.
In the controller class I created a action called "fetchFeUsersAction".
This action loads a set of data from the table fe_users in the table which was created from the extensionbuilder from the model.
The function to the get the users in the Repository looks like this:
public function getFeUsers()
{
/** #var Query $q */
$q = $this->createQuery();
$q->statement('SELECT * from fe_users');
$data = $q->execute(true);
return $data;
}
This works very fine.
But now I want to store the results from the table fe_users in my model with this action:
public function fetchFeUsersAction()
{
$data = $this->adressRepo->getFeUsers();
foreach ($data as $feuser) {
/** #var Adresse $address */
$address = $this->objectManager->get(Adresse::class);
$q= $this->adressRepo->createQuery();
$q->matching(contains('email', $feuser['email']));
$contain= $q->execute();
if($contain==NULL){
$address->setEmail($feuser['email']);
$this->adressRepo->add($address);
}
}
$this->redirect('list');
}
Here I want to check if the email adress is already stored in my table.
If the email adress is not stored it should be added to the model.
But it doesnt work. Even with an empty table. Without the condition it works very fine.
Extbase persists the complete data into the DB later. You need to persist manually i think, like this:
/** #var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager */
$persistenceManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
$persistenceManager->persistAll();
Not currently tested, just copied it from my of my extensions. but in there i had the same problem to check if it exists.
Hope this helps a little bit.
I have an extbase extension (TYPO3 7) with a simple model of a contact person.
The person has a name and a picture.
So far this is clear.
But every Person has a category (e.g. where he works. Office, Marketing etc.)
Therefor i use the system categories, as described here:
https://wiki.typo3.org/TYPO3_6.0#Adding_categories_to_own_models_without_using_Extension_Builder
When creating a person via web > list, i can assign a category.
Now the question for templating:
If i debug my contact person, i get the output like screen below.
I want to have a list where every category (headline) is shown with it's contact persons.
How to do this?
Is the logic for this only in the template or also in the controller?
Has anybody an example for this?
Best regards
Markus
I guess the required logic you need is possible with Fluid with using the GroupedFor ViewHelper and many others. Because a person can have multiple categories this would become a huge nesting of Viewhelpers so I can not recommend to use Fluid for this even if its possible. This kind of logics belong to the controllers, models and repositories.
There are multiple ways to solve this logic. Here is an example how to realize this in the controller...
Controller:
/**
* #var \TYPO3\CMS\Extbase\Domain\Repository\CategoryRepository
* #inject
*/
protected $categoryRespoitory = NULL;
/**
* action list
* #return void
*/
public function listAction()
{
$allCategories = $this->categoryRespoitory->findAll();
$categoriesWithContacts = [];
/** #var \TYPO3\CMS\Extbase\Domain\Model\Category $category */
foreach($allCategories as $category) {
$contactsInCategory= $this->contactRepository->findByCategory($category);
if($contactsInCategory->count()>0) {
$categoriesWithContacts[] = [
'category' => $category,
'contacts' => $contactsInCategory
];
}
}
$this->view->assignMultiple([
'categoriesWithContacts' => $categoriesWithContacts
]);
}
Injecting the CategoryRespository will required clearing cache in install tool or reinstalling the extension.
Maybe you need this function in your ContactRepository:
/**
* #param \TYPO3\CMS\Extbase\Domain\Model\Category $category
* #return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findByCategory(\TYPO3\CMS\Extbase\Domain\Model\Category $category) {
$query = $this->createQuery();
return $query->matching($query->contains('categories', $category))->execute();
}
Then in Fluid you can do something like this:
<f:for each="{categoriesWithContacts}" as="categoryWithContact">
{categoryWithContact.category.title}
<f:for each="{categoryWithContact.contacts}" as="contact">
{contact.name}
</f:for>
</f:for>
Scenario:
I have following model:
ContactPerson has a relation to FrontendUser and is the owning side of the relation. Now I have following problem:
I am activating/deactivating the FrontendUsers in a task, based on the ContactPersons which are active. When the FrontendUser is disabled or deleted the result of contactPerson->getFrontendUser() is null, even if both repositories ignoreEnableFields:
/** #var Typo3QuerySettings $querySettings */
$querySettings = $this->objectManager->get(Typo3QuerySettings::class);
$querySettings->setIgnoreEnableFields(true);
$querySettings->setRespectStoragePage(false);
$this->frontendUserRepository->setDefaultQuerySettings($querySettings);
$debugContactPerson = $this->contactPersonRepository->findOneByContactPersonIdAndIncludeDeletedAndHidden('634');
$debugFrontendUser = $this->frontendUserRepository->findOneByUid(7);
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(
array(
'$debugContactPerson' => $debugContactPerson,
'$debugFrontendUser' => $debugFrontendUser,
)
);
Result:
P.s.: $this->frontendUserRepository->findByUid(7); also doesn't work because it isn't using the query, but persistenceManager->getObjectByIdentifier(... which is of course ignoring the query-settings.
The problem is, in my real code I can't use findOneByUid(), because I can't get the integer-Value(uid) in the frontend_user field of the contact_person.
Any way to solve this without using a raw query to get the contact_person-row?
My (yes raw query) Solution:
Because I didn't want to write an own QueryFactory and I didn't want to add a redundant field to my contactPerson I solved it now with a raw statement. Maybe it can help someone with the same problem:
class FrontendUserRepository extends \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository
{
/**
* #param \Vendor\ExtKey\Domain\Model\ContactPerson $contactPerson
* #return Object
*/
public function findByContactPersonByRawQuery(ContactPerson $contactPerson){
$query = $this->createQuery();
$query->statement(
"SELECT fe_users.* FROM fe_users" .
" LEFT JOIN tx_extkey_domain_model_contactperson contact_person ON contact_person.frontend_user = fe_users.uid" .
" WHERE contact_person.uid = " . $contactPerson->getUid()
);
return $query->execute()->getFirst();
}
}
Invoking repository directly
There are two aspects for the enable fields of table fe_users:
$querySettings->setIgnoreEnableFields(true);
$querySettings->setEnableFieldsToBeIgnored(['disable']);
Have a look to some overview in the wiki page - it says 6.2, but it's still valid in most parts for 7.6 and 8 as well. However, this only works if the repository is invoked directly, but not if an entity is retrieved as part of another entity - in this case the repository is not used for nested entities.
Modify query settings for nested entities
Nested entities are retrieved implicitly - this happens in DataMapper::getPreparedQuery(DomainObjectInterface $parentObject, $propertyName). To adjust query settings for child entities, the QueryFactoryInterface implementation has to be overloaded.
Register an alternative implementation in ext_localconf.php (replace \Vendor\ExtensionName\Persistence\Generic\QueryFactory with the real class name of your extension):
$extbaseObjectContainer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
\TYPO3\CMS\Extbase\Object\Container\Container::class
);
$extbaseObjectContainer->registerImplementation(
\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class,
\Vendor\ExtensionName\Persistence\Generic\QueryFactory::class
);
With new Typo3 versions (v8+), the registerImplementation method no longer works for QueryFactory. Instead, a XCLASS must be used to overwrite/extend the class:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory::class] = [
'className' => \Vendor\ExtensionName\Persistence\Generic\QueryFactory::class,
];
Then inside the implementation:
<?php
namespace \Vendor\ExtensionName\Persistence\Generic;
use TYPO3\CMS\Extbase\Domain\Model\FrontendUser;
class QueryFactory extends \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory {
public function create($className) {
$query = parent::create($className);
if (is_a($className, FrontendUser::class, true)) {
// #todo Find a way to configure that more generic
$querySettings = $query->getQuerySettings();
$querySettings->setIgnoreEnableFields(true);
// ... whatever you need to adjust in addition ...
}
return $query;
}
}
My solution of this problem was to disable the "enablecolumns" in TCA definitions and deal this in the repository myself.
Here an example of findAll method:
public function findAll($ignoreEnableFields = false) {
$query = $this->createQuery();
if (!$ignoreEnableFields) {
$currTime = time();
$query->matching(
$query->logicalAnd(
$query->equals("hidden", 0),
$query->logicalOr(
$query->equals("starttime", 0),
$query->lessThanOrEqual("starttime", $currTime)
),
$query->logicalOr(
$query->equals("endtime", 0),
$query->greaterThanOrEqual("endtime", $currTime)
)
)
);
}
return $query->execute();
}