I created an extension with different MM-Relations. In example Products and Accessories. All accessories can be selected to every product. The problem is, if i select one accessory to one product the translation to another language works well. If i select one accessory to multiple products, the translation always uses the default language and no translation is shown. Could anyone please help me find a solution?
Im using Typo3 9.5
Many thanks.
public function findByUid($uid) {
$uidArray = explode(",", $uid);
$query = $this->createQuery();
$query->getQuerySettings()->setRespectSysLanguage(true);
foreach ($uidArray as $key => $value) {
$constraints[] = $query->equals('sys_language_uid', $value);
$constraints[] = $query->equals('uid', $value);
$constraints[] = $query->equals('l10n_parent', $value);
}
$query->matching(
$query->logicalAnd(
$query->logicalOr(
$constraints
),
$query->equals('hidden', 0),
$query->equals('deleted', 0)
)
);
}
Related
I have written an update query in TYPO3, Now I need to change it to query-object repository method. How to change the code below?
public function updatePaymentDetails($uid, $txnID, $amt, $stats)
{
$itemUID = $uid;
$transID = $txnID;
$amountPaid = $amt;
$txStatus = $stats;
$tableName = 'tx_paypalpayment_domain_model_transactions AS tpp';
$whereCondition = 'tpp.uid=' . '"' . $itemUID . '"';
$setValues = ['transactionid' => $transID, 'amount' => $amountPaid, 'txnstatus' => $txStatus];
$result = $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tableName, $whereCondition, $setValues);
return $result;
}
I created this much in my own idea (don't know it is correct or not), What should be the remaining portion?
public function paymentUpdate($uid, $txnID, $amt, $stats) {
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals("transactionid", $txnID),
$query->equals("amount", $amt),
$query->equals("txnstatus", $stats)
)
);
/*--- Update Code ---*/
return $query->execute();
}
Is there any way to do that?
The TYPO3/Extbase way is to first fetch your transaction from the persistence layer then apply your changes to the domain object and then update it in your repository.
Something like below in your controller action:
$transaction = $this->transactionRepository->findByIdentifier($itemUid);
$transaction->setTransactionId($transID);
$transaction->setAmount($amountPaid);
$transaction->setStatus($txStatus);
$this->transactionRepository->update($transaction);
If you wants to do a direct update instead of first fetching the record then take a look at the \TYPO3\CMS\Core\Database\Query\QueryBuilder (Only exists in newer versions of TYPO3 - 8.7 and above). In older versions of TYPO3 you could take a look at $GLOBALS['TYPO3_DB']->exec_*.
I am working on a component where I want to show all users of a specific usergroup. Right now I found two solutions for this but I'm not feeling comfortable with both of them.
Solution 1
$usersID = JAccess::getUsersByGroup(3);
$users = array();
foreach($usersID as $cUserID)
{
$users[] = JFactory::getUser($cUserID);
}
This one seems to produce two database queries every time JFactory::getUser($cUserID) is called. I really don't want this.
Solution 2
function inside model
function getUsers()
{
if(!isset($this->users))
{
$groupID = 3;
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$select = array( 'users.id', 'users.name');
$where = $db->quoteName('map.group_id') . ' = ' . $groupID;
$query
->select($select)
->from( $db->quoteName('#__user_usergroup_map', 'map') )
->leftJoin( $db->quoteName('#__users', 'users') . ' ON (map.user_id = users.id)' )
->where($where);
$db->setQuery($query);
$this->users = $db->loadObjectList();
}
return $this->users;
}
This one works like a charm but I feel there should be a "more Joomla! way" of doing this. I don't like working on their tables.
Right now I'm going with solution 2 but i really wonder if there is some better way to do it.
I am developing a TYPO3 6.0 plugin that shows the subpages of the current page as tabs. For example, on the following pages my plugin is inserted on TabRoot:
If TabRoot is requested, the plugin's ActionController looks up the database for the subpage titles and contents and passes all gathered data to a Fluid template. The page is then rendered like the following:
With JS in place I always hide/show content below based on the selection. My problem is that I want to show the translated content of the subpages based on the current language selection. How am I able to do this? I've tried it with several methods, but neither of them was flawless. These are the methods I've tried:
Using RECORDS This method is not affected by the selected language, it always returns the content in the default language:
//Get the ids of the parts of the page
$select_fields = "uid";
$from_table = "tt_content";
$where_clause = 'pid = ' . $pageId;
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
$select_fields,
$from_table,
$where_clause,
$groupBy='',
$orderBy='sorting',
$limit=''
);
$ids = '';
$firstIteration = true;
while ( $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc( $res ) ) {
if (!$firstIteration) $ids .= ",";
$ids .= $row ['uid'];
$firstIteration = false;
}
$GLOBALS['TYPO3_DB']->sql_free_result( $res );
//Render the parts of the page
$conf ['tables'] = 'tt_content';
$conf ['source'] = $ids;
$conf ['dontCheckPid'] = 1;
$content = $this->cObj->cObjGetSingle ( 'RECORDS', $conf );
Using CONTENTS According to TYPO3: How to render localized tt_content in own extension, this is the way to do it, however for me this also returns the content rendered with the default language. It is not affected by a language change.
$conf = array(
'table' => 'tt_content',
'select.' => array(
'pidInList' => $pageId,
'orderBy' => 'sorting',
'languageField' => 'sys_language_uid'
)
);
$content = $this->cObj->cObjGetSingle ( 'CONTENT', $conf );
Using VHS: Fluid ViewHelpers I installed the vhs extension and tried to render the content with <v:content.render />. The result is the same as with CONTENTS; it only works with the default language.
{namespace v=Tx_Vhs_ViewHelpers}
...
<v:content.render column="0" order="'sorting'" sortDirection="'ASC'"
pageUid="{pageId}" render="1" hideUntranslated="1" />
Using my own SQL query I've tried to get the bodytext fields of the page and then render those with \TYPO3\CMS\Frontend\Plugin\AbstractPlugin::pi_RTEcssText(). This method returns the content based on the current language, however the problem is that bodytext's do not contain the complete content (images, other plugins, etc).
$select_fields = "bodytext";
$from_table = "tt_content";
$where_clause = 'pid = ' . $pageId
. ' AND sys_language_uid = ' . $GLOBALS ['TSFE']->sys_language_uid;
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
$select_fields,
$from_table,
$where_clause,
$groupBy='',
$orderBy='sorting',
$limit=''
);
$content = '';
while ( $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc( $res ) ) {
$content .=
\TYPO3\CMS\Frontend\Plugin\AbstractPlugin::pi_RTEcssText( $row ['bodytext'] );
}
$GLOBALS['TYPO3_DB']->sql_free_result( $res );
What am I missing? Why isn't the content rendered with the current language in the case of the CONTENTS method?
Easiest way is to use the cObject viewhelper to render right from TypoScript.
And inside your TypoScript template provide the configuration:
lib.myContent = CONTENT
lib.myContent {
...
}
BTW, you are bypassing the TYPO3 CMS API. Please do not do so. Always use the API methods to query for data.
e.g. \TYPO3\CMS\core\Database\DatabaseConnection is always available at GLOBALS['TYPO3_DB']->. Do not use the the mysql function.
On top of that, I believe that you can archive whatever you are trying to do with pure TypoScript, without the need to program anything. Feel free to ask a new questions to get help on this.
In TYPO3 4.x you could use the following methods to load the translated record:
t3lib_pageSelect->getRecordOverlay
t3lib_pageSelect->getPageOverlay
They are also available at $GLOBALS['TSFE']->sys_page->getRecordOverlay().
I have a Symfony project and I used Zend Lucene Search framework to integrate a search on the site. It works beautifully but it's limited to searching 1 table.
I need my users to be able to search the whole site (8 select tables) and return the results all together. Each table has the same fields indexed. This is the code that specifies the table and calls the query.
Is there a way to make it look through all 8 tables for results?
public function getForLuceneQuery($query)
{
$hits = self::getLuceneIndex()->find($query);
$pks = array();
foreach ($hits as $hit)
{
$pks[] = $hit->pk;
}
if (empty($pks))
{
return array();
}
$alltables = Doctrine_Core::getTable('Car');
$q = $alltables->createQuery('j')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21);
return $q->execute();
}
To give a bit of background on the 8 tables, they are all basically similar. They all have title, make, model, etc so I need to run a single query on all of them and return all results (regardless of which table it is in) in Ascending order. The Doctrine_core::getTable command doesn't seem to like multiple tables or even arrays (unless I'm not doing it right). Thanks!
UPDATE (WORKING):
Here is the updated code. This is what I have in the SearchTable.class.php file:
public function getForLuceneQuery($query)
{
// sort search result by end time
$hits = self::getLuceneIndex()->find(
$query, 'endtime', SORT_NUMERIC, SORT_ASC
);
$result = array(
'index' => $hits,
'database' => array(),
);
// group search result by class
foreach ($hits as $hit)
{
if (!isset($result['database'][$hit->class]))
{
$result['database'][$hit->class] = array();
}
$result['database'][$hit->class][] = $hit->pk;
}
// replace primary keys with real results
foreach ($result['database'] as $class => $pks)
{
$result['database'][$class] = Doctrine_Query::create()
// important to INDEXBY the same field as $hit->pk
->from($class . ' j INDEXBY j.token')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21)
->execute();
}
return $result;
}
Here is what I have in the actions.class.php file for the Search Module:
public function executeIndex(sfWebRequest $request)
{
$this->forwardUnless($query = $request->getParameter('query'), 'home', 'index');
$this->results = Doctrine_Core::getTable('Search')
->getForLuceneQuery($query);
}
And finally this is my template file indexSuccess.php I have simplified it so it's easier to understand. My indexSuccess.php is more complicated but now that I can call the values, I can customize it further.
<div class="product_list"
<ul>
<?php foreach ($results['index'] as $hit): ?>
<li class="item">
<?php if (isset($results['database'][$hit->class][$hit->pk])) ?>
<span class="title">
<?php echo $results['database'][$hit->class][$hit->pk]->getTitle() ?>
</span>
</li>
<?php endforeach ?>
</ul>
</div>
This works beautifully. I was able to customize it by calling each of the fields in the search results and it works perfect. I added an item to each of the tables with the same title and the search result pulled them all. Thank you so much!
OK. I'll try to give you some hint, with code :)
First of all you should add these fields to the index:
$doc->addField(Zend_Search_Lucene_Field::Keyword('class', get_class($record)));
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('endtime', strtotime($record->get('endtime'))));
Than you should use these new fields:
public function getForLuceneQuery($query)
{
// sort search result by end time
$hits = self::getLuceneIndex()->find(
$query, 'endtime', SORT_NUMERIC, SORT_ASC
);
$result = array(
'index' => $hits,
'database' => array(),
);
// group search result by class
foreach ($hits as $hit)
{
if (!isset($result['database'][$hit->class]))
{
$result['database'][$hit->class] = array();
}
$result['database'][$hit->class][] = $hit->pk;
}
// replace primary keys with real results
foreach ($result['database'] as $class => $pks)
{
$result['database'][$class] = Doctrine_Query::create()
// important to INDEXBY the same field as $hit->pk
->from($class . ' j INDEXBY j.token')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21)
->execute();
// if you want different query per table
// you should call a function which executes the query
//
// if (!method_exists($table = Doctrine_Core::getTable($class), 'getLuceneSearchResult'))
// {
// throw new RuntimeException(sprintf('"%s::%s" have to be exists to get the search results.', get_class($table), 'getLuceneSearchResult'));
// }
//
// $results[$class] = call_user_func(array($table, 'getLuceneSearchResult'), $pks);
}
return $result;
}
After that in the template you should iterate over $result['index'] and display results from $result['database']
foreach ($result['index'] as $hit)
{
if (isset($result['database'][$hit->class][$hit->pk]))
{
echo $result['database'][$hit->class][$hit->pk];
}
}
And there are same alternate (maybe better) solutions that I can think of:
Alternate solution #1:
You can store data in the index and this data will be accessible in the search result. If you not need too much data when displaying the results and can update the index frequently I think this is a good option. This way you can use pagination and no SQL queries needed at all.
$doc->addField(Zend_Search_Lucene_Field::Text('title', $content->get('title')));
...
$hit->title;
Alternate solution #2:
As you wrote, your tables are very similar, so you maybe could use column aggregation inheritance. In this way all data stored in one table so you can query them all together and can order and paginate as you want.
iam trying to implement an Searchmachine into my site. Iam using Zend_Search_Lucene for this.
The index is created like this :
public function create($config, $create = true)
{
$this->_config = $config;
// create a new index
if ($create) {
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()
);
$this->_index = Zend_Search_Lucene::create(APPLICATION_PATH . $this->_config->index->path);
} else {
$this->_index = Zend_Search_Lucene::open(APPLICATION_PATH . $this->_config->index->path);
}
}
{
public function addToIndex($data)
$i = 0;
foreach ($data as $val) {
$scriptObj = new Sl_Model_Script();
$scriptObj->title = $val['title'];
$scriptObj->description = $val['description'];
$scriptObj->link = $val['link'];
$scriptObj->tutorials = $val['tutorials'];
$scriptObj->screenshot = $val['screenshot'];
$scriptObj->download = $val['download'];
$scriptObj->tags = $val['tags'];
$scriptObj->version = $val['version'];
$this->_dao->add($scriptObj);
$i++;
}
return $i;
}
/**
* Add to Index
*
* #param Sl_Interface_Model $scriptObj
*/
public function add(Sl_Interface_Model $scriptObj)
{
// UTF-8 for INDEX
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::text('title', $scriptObj->title, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('tags', $scriptObj->tags, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('version', $scriptObj->version, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('download', $scriptObj->download, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('link', $scriptObj->link));
$doc->addField(Zend_Search_Lucene_Field::text('description', $scriptObj->description, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('tutorials', $scriptObj->tutorials, 'utf-8'));
$doc->addField(Zend_Search_Lucene_Field::text('screenshot', $scriptObj->screenshot));
$this->_index->addDocument($doc);
}
But when i try to query the index with :
$index->find('Wordpress 2.8.1' . '*');
im getting the following error :
"non-wildcard characters are required at the beginning of pattern."
any ideas how to query for a string like mine ? an query for "wordpress" works like excepted.
Lucene cannot handle leading wildcards, only trailing ones. That is, it does not support queries like 'tell me everyone whose name ends with 'att'' which would be something like
first_name: *att
It only supports trailing wildcards. Tell me everyone whose names end that start with 'ma'
first_name: ma*
See this Lucene FAQ entry:
http://wiki.apache.org/lucene-java/LuceneFAQ#head-4d62118417eaef0dcb87f4370583f809848ea695
There IS a workaround for Lucene 2.1 but the developers say it can be "expensive".