Joomla: get all users in a usergroup - custom-component

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.

Related

How to write an extbase Repository-Method for Update in TYPO3?

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_*.

Translation in extension fails when not logged into backend

I have an extbase extension that translates some variables on pages using this
if (is_array($row) && $row['sys_language_uid'] != $GLOBALS['TSFE']->sys_language_content && $GLOBALS['TSFE']->sys_language_contentOL) {
$row = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $row,$GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
}
But there's a strange bug. It only works, when I'm logged into the backend. As soon as I log out, I can only see the default language.
Using Typo3 8.7
Any ideas what this could cause?
This can happen if you have config.sys_language_overlay = 0 defined globaly on your page object within your Typoscript Setup.
There is a ticket entry in the Typo3 Forge that goes a bit deeper as to why this happens and provides some workarrounds if you need those.
So, I didn't figure out what the problem was, but found my own workaround for the problem. Here's the updated Code:
$table = 'tx_myExt';
$query = "SELECT * FROM ".$table." WHERE pid=".$pid;
$query .= ' AND (sys_language_uid IN (-1,0) OR (sys_language_uid = ' .$GLOBALS['TSFE']->sys_language_uid. ' AND l10n_parent = 0))';
$query .= " AND deleted=0 AND hidden=0";
$res = $GLOBALS['TYPO3_DB']->sql_query($query);
while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
//localization
// check for language overlay if:
// * row is valid
// * row language is different from currently needed language
// * sys_language_contentOL is set
if (is_array($row) && $row['sys_language_uid'] != $GLOBALS['TSFE']->sys_language_content && $GLOBALS['TSFE']->sys_language_contentOL) {
$query2 = "SELECT * FROM ".$table." WHERE pid=".$pid;
$query2 .= ' AND sys_language_uid = ' .$GLOBALS['TSFE']->sys_language_content;
$query2 .= ' AND l10n_parent = ' . $row['uid'];
$query2 .= " AND deleted=0 AND hidden=0";
$res2 = $GLOBALS['TYPO3_DB']->sql_query($query2);
$row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res2);
//$row = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $row,$GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
}
}
So, instead of using getRecordOverlay I'm just repeating the query, accessing the translated record directly. I did read somewhere that this is bad practice, but hey, it works for now, that's all that matters ;)
Still, if someone finds a solution to the original problem, I'd be happy to see that.

How to get Filterable Attributes from a category in Magento 2

I have created category "Bag" in Magento 2. having filter attribute:
color
Size
I'm trying to get Filterable Attributes from category "Bag".
I have already done this in Magento 1.9:
Mage::app()->setCurrentStore($store);
$layer = Mage::getModel("catalog/layer");
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
But it does not seem to work for 2.x
I faced the same problem recently.
I documented my investigation here.
I was not able to find framework api to provide filterable attributes for specific category, however I will share workarounds.
Basically all filterable attributes in Magento 2 can be retrived from FilterableAttributeList:
$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);
$attributes = $filterableAttributes->getList();
Please use DI instead of ObjectManager::getInstance(). I used it just to have more compact example :)
Retrieving filters involved in layered navigation is a bit more tricky.
$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);
$appState = ObjectManager::getInstance()->get(\Magento\Framework\App\State::class);
$layerResolver = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Resolver::class);
$filterList = ObjectManager::getInstance()->create(
\Magento\Catalog\Model\Layer\FilterList::class,
[
'filterableAttributes' => $filterableAttributes
]
);
$category = 1234;
$appState->setAreaCode('frontend');
$layer = $layerResolver->get();
$layer->setCurrentCategory($category);
$filters = $filterList->getFilters($layer);
However, this is not the final result. To be sure that filters are actual, it is required to check number of items for each filters. (that check is actually performed during core layered navigation rendering)
$finalFilters = [];
foreach ($filters as $filter) {
if ($filter->getItemsCount()) {
$finalFilters[] = $filter;
}
}
Then you can get filter names and values. ie:
$name = $filter->getName();
foreach ($filter->getItems() as $item) {
$value = $item->getValue();
}
Finally, I would like to add alternative solution, that is a bit brutal, thought :)
$categoryId = 1234;
$resource = ObjectManager::getInstance()->get(\Magento\Framework\App\ResourceConnection::class);
$connection = $resource->getConnection();
$select = $connection->select()->from(['ea' => $connection->getTableName('eav_attribute')], 'ea.attribute_id')
->join(['eea' => $connection->getTableName('eav_entity_attribute')], 'ea.attribute_id = eea.attribute_id')
->join(['cea' => $connection->getTableName('catalog_eav_attribute')], 'ea.attribute_id = cea.attribute_id')
->join(['cpe' => $connection->getTableName('catalog_product_entity')], 'eea.attribute_set_id = cpe.attribute_set_id')
->join(['ccp' => $connection->getTableName('catalog_category_product')], 'cpe.entity_id = ccp.product_id')
->where('cea.is_filterable = ?', 1)
->where('ccp.category_id = ?', $categoryId)
->group('ea.attribute_id');
$attributeIds = $connection->fetchCol($select);
Then it is possible to use attribute ids to load collection.
/** #var $collection \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection */
$collection = $this->collectionFactory->create();
$collection->setItemObjectClass('Magento\Catalog\Model\ResourceModel\Eav\Attribute')
->addStoreLabel($this->storeManager->getStore()->getId());
$collection->addFieldToFilter('attribute_id', ['in' => $attributeIds]);
If you know how to build module then you can take help from 'FiltersProvider.php' from 'module-catalog-graph-ql\Model\Resolver\Layer'.
use Magento\Catalog\Model\Layer\Category\FilterableAttributeList as CategoryFilterableAttributeList;
use Magento\Catalog\Model\Layer\FilterListFactory;
use Magento\Catalog\Model\Layer\Resolver;
use Magento\Framework\UrlInterface;
public function __construct(
Resolver $layerResolver,
FilterListFactory $filterListFactory,
CategoryFilterableAttributeList $categoryFilterableAttributeList,
UrlInterface $urlBuilder
) {
$this->_navigation = $navigation;
$this->layerResolver = $layerResolver;
$this->filterListFactory = $filterListFactory;
$this->urlBuilder = $urlBuilder;
$this->_categoryFilterableAttributeList = $categoryFilterableAttributeList;
}
public function getCatMenu($catid)
{
$fill_arr = [];
$filterList = $this->filterListFactory->create(['filterableAttributes' => $this->_categoryFilterableAttributeList]);
$layer = clone $this->layerResolver->get();
$layer->setCurrentCategory($catid);
$filters = $filterList->getFilters($layer);
return $fill_arr;
}

How to write native query in Doctrine / Postgres equivalent to a normal PDO_PGSQL

I have the following query in php with (pdo_pgsql) adapter:
$dsn = "pgsql:host=$host;port=5432;dbname=$db;user=$username;password=$password";
$conn = new PDO($dsn);
$query = 'SET search_path TO xyz_de;';
$result = $conn->query($query);
$query = 'Select * FROM "Orders" WHERE vendor_id=189 ';
$statement = $conn->query($query);
while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
var_dump($row); // --> working perfectly fine, I see results here
}
I want to reproduce it using doctrine so I'm doing the following:
// given: 1) $this->em = EntityManager
// 2) the conenction is established successfull (I debugged it and sure about it)
$rsm = new ResultSetMapping();
$query = $this->em->createNativeQuery("SET search_path TO xyz_de ", $rsm);
$query->execute();
$query = $this->em->createNativeQuery('SELECT * FROM "Orders" WHERE vendor_id=?', $rsm);
$query->setParameter(1, 189);
$orders = $query->getArrayResult();
var_dump($orders); // Gives empty array (no errors)
as per my comment in the last line, the problem is that I get no results and no errors when using doctrine for this
Many things:
You can use addScalarResult
A simple example:
$rsm->addScalarResult('my_real_column', 'my_alias_column');
$query = $this->em->createNativeQuery('SELECT my_real_column FROM "Orders"');
var_dump($query->getScalarResult());
But I think, the best you can do is to use the queryBuilder.

How to prevent SQL injection in PhalconPHP when using sql in model?

Let's say I am building a search that finds all the teacher and got an input where the user can put in the search term. I tried reading the phalcon documentation but I only see things like binding parameters. I read the other thread about needing prepare statements do I need that in Phalcon as well?
And my function in the model would be something like this:
public function findTeachers($q, $userId, $isUser, $page, $limit, $sort)
{
$sql = 'SELECT id FROM tags WHERE name LIKE "%' . $q . '%"';
$result = new Resultset(null, $this,
$this->getReadConnection()->query($sql, array()));
$tagResult = $result->toArray();
$tagList = array();
foreach ($tagResult as $key => $value) {
$tagList[] = $value['id'];
....
}
}
My question is for the Phalcon framework is there any settings or formats I should code for this line $sql = 'SELECT id FROM tags WHERE name LIKE "%' . $q . '%"';
And also any general recommendation for preventing SQL Injection in PhalconPHP controllers and index would be appreciated.
For reference:
My controller:
public function searchAction()
{
$this->view->disable();
$q = $this->request->get("q");
$sort = $this->request->get("sort");
$searchUserModel = new SearchUsers();
$loginUser = $this->component->user->getSessionUser();
if (!$loginUser) {
$loginUser = new stdClass;
$loginUser->id = '';
}
$page = $this->request->get("page");
$limit = 2;
if (!$page){
$page = 1;
}
$list = $searchUserModel->findTeachers($q, $loginUser->id, ($loginUser->id)?true:false, $page, $limit, $sort);
if ($list){
$list['status'] = true;
}
echo json_encode($list);
}
My Ajax:
function(cb){
$.ajax({
url: '/search/search?q=' + mapObject.q + '&sort=<?php echo $sort;?>' + '&page=' + mapObject.page,
data:{},
success: function(res) {
//console.log(res);
var result = JSON.parse(res);
if (!result.status){
return cb(null, result.list);
}else{
return cb(null, []);
}
},
error: function(xhr, ajaxOptions, thrownError) {
cb(null, []);
}
});
with q being the user's search term.
You should bind the query parameter to avoid an SQL injection. From what I can remember Phalcon can be a bit funny with putting the '%' wildcard in the conditions value so I put them in the bind.
This would be better than just filtering the query.
$tags = Tags::find([
'conditions' => 'name LIKE :name:',
'bind' => [
'name' => "%" . $q . "%"
]
])
Phalcon\Filter is helpful when interacting with the database.
In your controller you can say, remove everything except letters and numbers from $q.
$q = $this->request->get("q");
$q = $this->filter->sanitize($q, 'alphanum');
The shortest way for requests:
$q = $this->request->get('q', 'alphanum');