TYPO3: Translated mm records in command controller - typo3

I am trying to get the translated records from the command controller by calling a function from extension repository like this.
function findAllForLang($lang){
//$lang = 1;
$query = $this->createQuery();
$query->getQuerySettings()->setRespectSysLanguage(false);
$query->getQuerySettings()->setLanguageMode(false);
$query->getQuerySettings()->setLanguageUid($lang);
$query->getQuerySettings()->setLanguageOverlayMode('hideNonTranslated');
$query->matching(
$query->logicalAnd(
$query->equals('sys_language_uid', $lang),
//$query->equals('mmfield.sys_language_uid', $lang)
)
);
return $query->execute();
I am getting the translated records. But the mm inline records are not translated and getting the default language records.
Is there any specific way to get all the translated inline records ?
TYPO3 version is 8.7.20
Thank you

There is a longstanding bug regarding fetching of translated relations with Extbase: https://forge.typo3.org/issues/57272
It was fixed for TYPO3 v9+.
For your case I suggest to build the query yourself with the (newer) doctrine-dbal API:
https://docs.typo3.org/m/typo3/reference-coreapi/8.7/en-us/ApiOverview/Database/QueryBuilder/Index.html

Related

Select records from other languages in a scheduler task

I am developing an extension, which provides a scheduler task. In this task an API is triggered and the results are going to be written to the database. So far there is no problem.
But the whole typo3 (8.7 with php7.2) page have multiple languages. So I have to save all the API objects in their different language. This does not make any difficulties for the insert process, but in the update process I could not select the correct languages.
Some mysterios things I still solved, like the sys_language_uid parameter, which have to be _languageUid (more details) or that I do not find any solution for getting all possible languages from the sys_language table without using a own doctrine querybuilder over the ConnectionPool. (I also add the default language as an entry)
Now to the main problem, which I could not solve yet. After debugging the core I get, that the queries I build with the repository are correct.
the repository:
$query = $this->createQuery();
$query->getQuerySettings()->setIgnoreEnableFields(true);
$query->getQuerySettings()->setRespectSysLanguage(false);
$query->matching(
$query->logicalAnd(
$query->equals('identifier', $identifier),
$query->equals('sys_language_uid', $languageUid)
)
);
the resulting query is:
SELECT `TABLE`.* FROM `TABLE` `T`
WHERE (
(`T`.`identifier` = :dcValue1)
AND (`T`.`sys_language_uid` = :dcValue2)
) AND (`T`.`deleted` = 0)
Now I try to get the parent object from the database to proceed an update. The whole update-mapping is unit-tested and all debug outputs confirm that everything works fine.
I look back to core and find at TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend::class on line 612:
foreach ($rows as $row) {
// If current row is a translation select its parent
if (isset($tableName) && isset($GLOBALS['TCA'][$tableName])
&& isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
&& isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
&& $tableName !== 'pages_language_overlay'
) {
if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
&& $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
) {
$queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
$queryBuilder->getRestrictions()->removeAll();
$row = $queryBuilder->select($tableName . '.*')
->from($tableName)
->where(
$queryBuilder->expr()->eq(
$tableName . '.uid',
$queryBuilder->createNamedParameter(
$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
\PDO::PARAM_INT
)
),
$queryBuilder->expr()->eq(
$tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'],
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
)
)
->setMaxResults(1)
->execute()
->fetch();
}
}
...
Like the comment on line 613 says, the row from the database will be overwritten in this foreach. All xdebug-data confirm that. If I disable the transOrigPointerField in my TCA it will work like expected, but the backend-view could not assign the multiple languages to one parent element.
I also tried to delete the TCA entry just for the scheduler task, but the TCA is cached and no temporary overwrite is possible.
Am I doing something wrong, or is it just a bug?
update
Take a look at the simplified example
I was editing the TYPO3-core for version 8.7 (branch TYPO3_8-7 after tag 8.7.19) now. At the file typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php on line 618 I additonally check the respectSysLangauge option so the new check will be:
// If current row is a translation select its parent
if (isset($tableName) && isset($GLOBALS['TCA'][$tableName])
&& isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
&& isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
&& $tableName !== 'pages_language_overlay'
&& true === $querySettings->getRespectSysLanguage()
) {
After this I started the TYPO3-testsuite for this part of TYPO3 and everthing seems to be OK (all tests passed) and my problem is fixed. I dont know how to contribute correctly without spending hours with reading the whole contribution guide.

Aggregate Functions in extbase typo3

I am using extbase-query-method ( like , in ,contains ).
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals("deleted", "0"),
$query->equals("hidden", "0"),
$query->equals("status", $status)
)
);
return $query->execute();
but how can i used Aggregate Functions in extbase query statement.
As far as I know, Extbase doesn't support the use of aggregate functions in the ORM. See more here: https://docs.typo3.org/typo3cms/ExtbaseFluidBook/6-Persistence/3-implement-individual-database-queries.html
You will have to build the SQL query yourself (in the statement) and to tell the query to return raw result (that is stored in an array) instead of returning an object based query.
Exemple:
$query = $this->createQuery();
$query->statement("SELECT AVG(clicks) as 'avg' FROM tablename ...");
$query->getQuerySettings()->setReturnRawQueryResult(TRUE);
$result = $query->execute()[0][avg];
Cheers,
Olivier
I think the only solution for Extbase Query is
$query->statement('SELECT AVG(clicks) FROM tablename ...')
Or not?
If you use Plain SQL Statements, you have can get problems with sql injection!
A better way should be using the new doctrine querybuilder which is since 8.7 available:
// SELECT COUNT(`uid`) FROM `tt_content` WHERE (`bodytext` = 'klaus')
// AND ((`tt_content`.`deleted` = 0) AND (`tt_content`.`hidden` = 0)
// AND (`tt_content`.`starttime` <= 1475580240)
// AND ((`tt_content`.`endtime` = 0) OR (`tt_content`.`endtime` > 1475580240)))
$count = $queryBuilder
->count('uid')
->from('tt_content')
->where(
$queryBuilder->expr()->eq('bodytext', $queryBuilder->createNamedParameter('klaus'))
)
->execute()
->fetchColumn(0);
https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html
As others have pointed out, you cannot use these functions on the Query object itself. However, you could use the ExpressionBuilder class for something like this:
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('my_awesome_table');
$statement = $queryBuilder
->select('uid')
->addSelectLiteral(
$queryBuilder->expr()->max('interesting_field', 'awesome_alias')
)
->from('my_awesome_table')
->groupBy('interesting_field')
->execute();
Note that this returns a QueryResult object.
The only function you can use is COUNT() with
return $query->execute()->count();
For all other functions you have to do a native query, like described in other answers

How to avoid affecting other queries when using posts_orderby?

In WordPress as you must already known, when using get_posts() or query_posts() or even WP_Query, it is not possible to order the returned posts by specifying a list of post ID in the order we want.
Instead we have to loop through the results and re-order them on the PHP side. This is a performance hit and a bad practice. Instead we should use built-in MySQL functions to retrieve the posts in the desired order upfront.
Thankfully there is the posts_orderby which can be used to specify a custom ORDERBY statement, like this:
// My list of post IDs in my custom order
$my_post_ids = array(1,3,2);
// Apply filter to the ORDERBY SQL statement
add_filter('posts_orderby', 'my_custom_orderby');
function my_custom_orderby($orderby_statement) {
global $my_post_ids;
$orderby_statement = 'FIELD(ID, '.implode(',',$my_post_ids).')';
return $orderby_statement;
}
// My custom query
$my_custom_query = new WP_Query(array('post_type' => 'post', 'post__in' => $my_post_ids);
However there is a problem with the above code, is that it will affect the order of all queries on the page! Including queries made by plugins, shortcodes, and so on.
Easy fix!
The simple way to fix this, is to apply the filter only one time, and remove it as soon as it is called, by putting a remove_filter() within the filter itself, so it is run only once:
// My list of post IDs in my custom order
$my_post_ids = array(1,3,2);
// Apply filter to the ORDERBY SQL statement
add_filter('posts_orderby', 'my_custom_orderby');
function my_custom_orderby($orderby_statement) {
// Disable this filter for future queries!
remove_filter(current_filter(), __FUNCTION__);
global $my_post_ids;
$orderby_statement = 'FIELD(ID, '.implode(',',$my_post_ids).')';
return $orderby_statement;
}
// My custom query
$my_custom_query = new WP_Query(array('post_type' => 'post', 'post__in' => $my_post_ids);
Because I set this filter just before my custom query, once I execute my custom query it should be filtered by the posts_orderby filter set above, which is then immediately disabled so it won't affect any future queries.
In theory, that's great, and it works great in most case scenarios!
An issue with WPML
However I have encountered a case when using the WPML plugin where this filter affects other queries than mine and causes errors. I believe the WPML plugin is creating a query of its own that is executed just before my own custom query, making my filter applies to the WPML query instead of mine!
Is there any possible way to add a check within the filter to make sure that it affects the correct query?
Thank you very much
Edit:
The fix for WPML
For information, while the accepted answer for this question is correct, it didn't solve the problem I was having with WPML. Here is how I fixed the WPML conflict:
// My list of post IDs in my custom order
$my_post_ids = array(1,3,2);
// Apply filter to the ORDERBY SQL statement
add_filter('posts_orderby', 'my_custom_orderby');
function my_custom_orderby($orderby_statement) {
// Disable this filter for future queries!
remove_filter(current_filter(), __FUNCTION__);
global $my_post_ids, $wpdb;
$orderby_statement = 'FIELD('.$wpdb->base_prefix.'posts.ID, '.implode(',',$my_post_ids).')';
return $orderby_statement;
}
// My custom query
$my_custom_query = new WP_Query(array('post_type' => 'post', 'post__in' => $my_post_ids);
This filter takes two parameters, $orderby and &$this. "this" being the WP_Query object. I'm not sure how to detect that WPML is making the call, but we can check that your call is the one being
made.
$my_post_ids = array(1,3,2);
add_filter( 'posts_orderby', 'my_custom_orderby', 10, 2 );
function my_custom_orderby( $orderby_statement, $object )
{
global $my_post_ids;
if( $my_post_ids != $object->query['post__in'] )
return $orderby_statement;
// Disable this filter for future queries!
remove_filter( current_filter(), __FUNCTION__ );
$orderby_statement = 'FIELD(ID, ' . implode( ',', $my_post_ids ) . ')';
return $orderby_statement;
}

zend framework 1.11.5 is choking on search function - mysql db

ZF 1.11.5 is puking all over this search function. i've tried creating the query several different ways, sent the sql statement to my view, copied and pasted the sql statement into phpMyAdmin and successfully retrieved records using the sql that ZF is choking on. i have been getting a coupld of different errors: 1) an odd SQL error about 'ordinality' (from my Googling ... it seems this is a ZF hang up .. maybe?) and 2) Fatal error: Call to undefined method Application_Model_DbTable_Blah::query() in /blah/blah/blah.php on line blah
public function searchAction($page=1)
{
$searchForm = new Application_Model_FormIndexSearch();
$this->view->searchForm = $searchForm;
$this->view->postValid = '<p>Enter keywords to search the course listings</p>';
$searchTerm = trim( $this->_request->getPost('keywords') );
$searchDb = new Application_Model_DbTable_Ceres();
$selectSql = "SELECT * FROM listings WHERE `s_coursedesc` LIKE '%".$searchTerm."%' || `s_title` LIKE '%".$searchTerm."%'";
$selectQuery = $searchDb->query($selectSql);
$searchResults = $selectQuery->fetchAll();
}
here's my model ....
class Application_Model_DbTable_Ceres extends Zend_Db_Table_Abstract
{
protected $_name = 'listings';
function getCourse( $courseId )
{
$courseid = (int)$courseId;
$row = $this->fetchRow('id=?',$courseId);
if (!$row)
throw new Exception('That course id was not found');
return $row->toArray();
}
}
never mind the view file ... that never throws an error. on a side note: i'm seriously considering kicking ZF to the curb and using CodeIgniter instead.
looking forward to reading your thoughts. thanks ( in advance ) for your responses
You're trying to all a method called query() on Zend_Db_Table but no such method exists. Since you have built the SQL already you might find it easier to call the query on the DB adapter directly, so:
$selectSql = "SELECT * FROM listings WHERE `s_coursedesc` LIKE '%".$searchTerm."%' || `s_title` LIKE '%".$searchTerm."%'";
$searchResults = $selectQuery->getAdapter()->fetchAll($selectSql);
but note that this will give you arrays of data in the result instead of objects which you might be expecting. You also need to escape $searchTerm here since you are getting that value directly from POST data.
Alternatively, you could form the query programatically, something like:
$searchTerm = '%'.$searchTerm.'%';
$select = $selectQuery->select();
$select->where('s_coursedesc LIKE ?', $searchTerm)
->orWhere('s_title LIKE ?', $searchTerm);
$searchResults = $searchQuery->fetchAll($select);

Help needed formatting Doctrine Query in Zend Framework

Can anyone tell me how to format the query below correctly in my controller.
Currently it gives me nothing in my FilteringSelect. However if I change it to >= I get back all the kennelIDs which is incorrect also but at least I'm getting something.
I've tested that the session variable is set and can confirm that there are kennels with the matching capacity.
// Create autocomplete selection for the service of this booking
public function servkennelAction()
{
$sessionKennelBooking = new Zend_Session_Namespace('sessionKennelBooking');
// disable layout and view rendering
$this->_helper->layout->disableLayout();
$this->getHelper('viewRenderer')->setNoRender(true);
// get list of grooming services for dogs from the table
$qry= Doctrine_Query::create()
->from('PetManager_Model_Kennels k');
//This should be set by default and narrows down the search criteria
if(isset($sessionKennelBooking->numPets)){
$b=(int)$sessionKennelBooking->numPets;
$qry->addWhere('k.capacity = ?','$b');
}
$result=$qry->fetchArray();
//generate and return JSON string using the primary key of the table
$data = new Zend_Dojo_Data('kennelID',$result);
echo $data->toJson();
}
Many thanks in Advance.
Graham
I think that addWhere condition is wrong. It has to be:
$qry->addWhere('k.capacity = ?', $b);
i.e. $b without quotes.