How to add "order by" on a Magento Query - zend-framework

I installed a script for magento. It allows to add comments on orders. So it shows ONE comment on order grid.
The problem is that it doesn't sort comment by "created_at" column. I don't know how to set the order.
This is the portion of code:
protected function _initSelect()
{
parent::_initSelect();
// Join order comment
$this->getSelect()->joinLeft(
array('ordercomment_table' => $this->getTable('sales/order_status_history')),
'main_table.entity_id = ordercomment_table.parent_id AND ordercomment_table.comment IS NOT NULL',
array(
'ordercomment' => 'ordercomment_table.comment',
)
)->group('main_table.entity_id');
return $this;
}
Thanks for your help.

protected function _initSelect()
{
parent::_initSelect();
// Join order comment
$this->getSelect()->joinLeft(
array('ordercomment_table' => $this->getTable('sales/order_status_history')),
'main_table.entity_id = ordercomment_table.parent_id AND ordercomment_table.comment IS NOT NULL',
array(
'ordercomment' => 'ordercomment_table.comment',
)
)->group('main_table.entity_id');
//Add ORDER BY
$this->getSelect()->order(array('ordercomment_table.created_at DESC'));
return $this;
}

Replace this line in the above existing function. Add an ORDER BY.
$this->getSelect()->order('ordercomment_table.created_at', 'DESC');

Related

Query with orderings causing empty result - Extbase 6.2

I made an extbase extension and want to list my appointments ordered first by startDate and for those appointments that are on the same day I want to order them by the last name of the customer.
In my repository I made following working query:
public function findAppointmentsForList($future) {
$curtime = time();
$query = $this->createQuery();
$constraints = array();
if ($future !== NULL) {
$constraints[] = ($future) ?
$query->greaterThanOrEqual('startDate', $curtime) :
$query->lessThan('startDate', $curtime);
}
if ($constraints) {
$query->matching($query->logicalAnd($constraints));
} else {}
$orderings = array(
'startDate' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
// 'customer.lastName' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
$query->setOrderings($orderings);
return $query->execute();
}
It returns me some appointments so I assume it's working.
If I then uncomment the line 'customer.lastName... it returns 0 appointments.
What's going on? It's just the ordering, it can't possibly make the query smaller...
I don't even get any errors - I tried it with an invalid property for example and it gave me an error, so the property name is correct too.
And I debugged the working query and the last names in those customer objects where there.
This is my appointment model entry:
/**
* customer
*
* #var \vendor\extension\Domain\Model\Customer
*/
protected $customer = NULL;
And this is the TCA corresponding to it:
'customer' => array(
'exclude' => 1,
'label' => 'LLL:EXT:extension/Resources/Private/Language/locallang_db.xlf:tx_extension_domain_model_appointment.customer',
'config' => array(
'type' => 'select',
'foreign_table' => 'fe_users',
'minitems' => 0,
'maxitems' => 1,
),
),
EDIT: It's working now...but unfortunately I don't know why, changed too much in the meantime which I thought had nothing to do with this. One thing that might've affected this: startDate is of type Date and I noticed the query didn't filter it right so after I changed the curtime to new \DateTime('midnight') it was filtering correctly.
When you use related models in a query as part of the matching or orderBy then the query will be build with joins to the related tables. That means, that the result is actually smaller and will not include appointments without customers.
The strange thing is that you see the last names when you debug it, otherwise i would assume that you have some configuration errors in the TCA. Can you provide the TCA code from the apointment table and may be its model?

Security Group subpanel doesn't exist for quotes, contrats, invoices, and events modules

I am using suiteCRM 7.7.4 (Sugar Version 6.5.24) and I need to use Security group subpanel in quotes, contracts, invoices and events modules, but for some reasons I can't find it ! I did some researches and I found that this subpanel doesn't appear by default for custom modules.. some developers recommand to do not use the studio to build this kind of relationship, because simply it will not work ! for paid version of sugarCRM they say that there was a tool called "hookup tool" that creates the relationship for you... but As I am using a free version I can't use it !
Do you have any idea ?
Thank you very much !
I finaly find a solution :
Adding this few lines to "modules/AOS_Contracts/metadata/subpaneldefs.php" :
'securitygroups' => array(
'top_buttons' => array(array('widget_class' => 'SubPanelTopSelectButton', 'popup_module' => 'SecurityGroups', 'mode' => 'MultiSelect'),),
'order' => 900,
'sort_by' => 'name',
'sort_order' => 'asc',
'module' => 'SecurityGroups',
'refresh_page' => 1,
'subpanel_name' => 'default',
'get_subpanel_data' => 'SecurityGroups',
'add_subpanel_data' => 'securitygroup_id',
'title_key' => 'LBL_SECURITYGROUPS_SUBPANEL_TITLE',
),
QRR
Verifying permissions.
follow this following steps:
1. Go to Admin
2. Go to studio
3. Select your module where you want subpanel like "invoices"
4. Go to relationship
5. Add 1 to many relationship with Security group module.
6. Now repair rebuild you will find the subapnel in invoice module.
When you create 1 to many relationship with any module it will create the subpanel.
IF its not working then go for custom subpanel.
Refer this linnk I put code from same link it worked for me
This tutorial should hopefully help you to create a new subpanel under the Contacts module in Sugar using a custom link class and driven by SugarCRM 7's new SugarQuery API.
Create a new link class
This should go into custom/modules/<YourModule>/YourNewLink.php and this class will act as the custom functionality that will build your link between the two records.
<?php
/**
* Custom filtered link
*/
class YourNewLink extends Link2
{
/**
* DB
*
* #var DBManager
*/
protected $db;
public function __construct($linkName, $bean, $linkDef = false)
{
$this->focus = $bean;
$this->name = $linkName;
$this->db = DBManagerFactory::getInstance();
if (empty($linkDef)) {
$this->def = $bean->field_defs[$linkName];
} else {
$this->def = $linkDef;
}
}
/**
* Returns false if no relationship was found for this link
*
* #return bool
*/
public function loadedSuccesfully()
{
// this link always loads successfully
return true;
}
/**
* #see Link2::getRelatedModuleName()
*/
public function getRelatedModuleName()
{
return '<Your_Module>';
}
/**
*
* #see Link2::buildJoinSugarQuery()
*/
public function buildJoinSugarQuery($sugar_query, $options = array())
{
$joinParams = array('joinType' => isset($options['joinType']) ? $options['joinType'] : 'INNER');
$jta = 'active_other_invites';
if (!empty($options['joinTableAlias'])) {
$jta = $joinParams['alias'] = $options['joinTableAlias'];
}
$sugar_query->joinRaw($this->getCustomJoin($options), $joinParams);
return $sugar_query->join[$jta];
}
/**
* Builds main join subpanel
* #param string $params
* #return string JOIN clause
*/
protected function getCustomJoin($params = array())
{
$bean_id = $this->db->quoted($this->focus->id);
$sql = " INNER JOIN(";
$sql .= "SELECT id FROM accounts WHERE id={$bean_id}"; // This is essentially a select statement that will return a set of ids that you can match with the existing sugar_query
$sql .= ") accounts_result ON accounts_result.id = sugar_query_table.id";
return $sql;
}
}
The argument $sugar_query is a new SugarQuery object, the details of which are documented here. What you essentially need to do is extend this query with whatever join/filters you wish to add. This is done in the inner join I've specified.
Note: The inner join can get really complicated, so if you want a real working example, checkout modules/Emails/ArchivedEmailsLink.php and how the core sugar team use this. I can confirm however that this does work with custom joins.
Here is the getEmailsJoin to help you understand what you can actually produce via this custom join.
/**
* Builds main join for archived emails
* #param string $params
* #return string JOIN clause
*/
protected function getEmailsJoin($params = array())
{
$bean_id = $this->db->quoted($this->focus->id);
if (!empty($params['join_table_alias'])) {
$table_name = $params['join_table_alias'];
} else {
$table_name = 'emails';
}
return "INNER JOIN (\n".
// directly assigned emails
"select eb.email_id, 'direct' source FROM emails_beans eb where eb.bean_module = '{$this->focus->module_dir}'
AND eb.bean_id = $bean_id AND eb.deleted=0\n" .
" UNION ".
// Related by directly by email
"select DISTINCT eear.email_id, 'relate' source from emails_email_addr_rel eear INNER JOIN email_addr_bean_rel eabr
ON eabr.bean_id = $bean_id AND eabr.bean_module = '{$this->focus->module_dir}' AND
eabr.email_address_id = eear.email_address_id and eabr.deleted=0 where eear.deleted=0\n" .
") email_ids ON $table_name.id=email_ids.email_id ";
}
Add a new vardef entry for the link field.
For this example, I'm going to create the custom link on the contacts module. So this code goes in custom/Extension/modules/Contacts/Ext/Vardefs/your_field_name.php
<?php
$dictionary["Contact"]["fields"]["your_field_name"] = array(
'name' => 'active_other_invites',
'type' => 'link',
'link_file' => 'custom/modules/<YourModule>/YourNewLink.php',
'link_class' => 'YourNewLink',
'source' => 'non-db',
'vname' => 'LBL_NEW_LINK',
'module' => '<YourModule>',
'link_type' => 'many',
'relationship' => '',
);
Add the new link as a subpanel
This goes under custom/Extension/modules/Contacts/Ext/clients/base/layouts/subpanels/your_subpanel_name.php
<?php
$viewdefs['Contacts']['base']['layout']['subpanels']['components'][] = array (
'layout' => 'subpanel',
'label' => 'LBL_NEW_LINK',
'context' =>
array (
'link' => 'your_field_name',
),
);
Add the label
Under
custom/Extension/modules/Contacts/Ext/Language/en_us.new_link.php
<?php
$mod_strings['LBL_ACTIVE_OTHER_INVITES'] = 'Your New Link';
Quick Repair and Rebuild
That should hopefully get you started. Keep an eye on the sugarlogs while you're debugging your queries. I also found using xdebug and SugarQueries compileSql function invaluable in figuring out what I needed to do to get a working INNER JOIN statement.
I've found this to be a surprisingly powerful solution, it means that if you need to show information related to a module that might be a few joins away, this allows you to create the links manually without having to create pointless related fields in-between the two.

Sonata Admin MongoDB DataGrid filter

Situation
We are running Symfony 2.8 and the latest version of Sonata Admin along with Mongo as a data store. Please consider the following object which has been simplified for the sake of this question; it does work.
class Entry
{
/* #ID */
protected $id;
/* #String */
protected $type;
/* #String */
protected $content;
}
With the above, there will be lots of entries and from the Admin itself, we would like to filter by type.
Here's an example of the dataset
Problem
We can't create a set of selectable filters in the dataGrid function which are UNIQUE for type.
Attempts
Note that where needed, the EntryRepository is included as a namespace at the start of the file
NUMBER 1
With the below, we get the type duplicated many times
->add('type', null, array(), 'document', array(
'expanded' => true,
'class' => 'Application:Entry',
'query_builder' => function(EntryRepository $dr) {
return $dr->createQueryBuilder();
}
))
NUMBER 2
With the below, we get a 500 error with only the message "string". I think this is because when using distinct, Mongo prepares a set of arrays instead of unexecuted QueryBuilder object?
->add('type', null, array(), 'document', array(
'expanded' => true,
'class' => 'Application:Entry',
'query_builder' => function(Entryepository $dr) {
return $dr->createQueryBuilder()
->distinct('type');
}
))
NUMBER 3
The attempt below is to use Map reduce to perform the equivalent of an SQL "GROUP BY" however, the same STRING error as above is provided.
->add('type', '', array(), 'document', array(
'expanded' => true,
'class' => 'Application:Entry',
'query_builder' => function(EntryRepository $dr) {
return $dr->createQueryBuilder()
->group(array(), array('type'))
->reduce('function (obj, prev) { prev.type; }');
}
))
CRUDE WORKAROUND...discouraged
The below is a demonstration using the filter (as listed in the Sonata documentation) and it DOES work...for one type at a time.
->add('type', 'doctrine_mongo_callback', array(
'callback' => function($queryBuilder, $alias, $field, $value) {
if (!$value || $value['value'] == false) {
return true;
}
$queryBuilder
->field('type')->equals('fds');
return true;
},
'field_type' => 'checkbox'
))
Taken this approach, I think I'd have to go ahead and query the whole dataset getting the distinct values for type and then loop around each constructing the filter. This would work but be horribly messy.
QUESTION
What is the "best practise" way of performing this without turning the code in to a ruddy mess? Putting the query in a repository will still go and create a similar effect?
Thanks for reading
I'm posting the answer for anyone else who may be facing the same issue. I realised I was approaching the situation from the wrong angle and instead of any of the above, did the following
/* Prepare the options to be used in the filter */
$tagRepo = $this->getConfigurationPool()->getContainer()->get('repository.tag');
$types = $tagRepo->findTypes();
$choices = array();
/* Create a simple choice compatible array */
foreach ($types as $type) { $choices[$type] = $type; }
/* In the actial filter itself */
$datagridMapper
->add('type', 'doctrine_mongo_choice', array(), 'choice',
array('choices' => $choices))

yii2-mongodb: How i can make the has many or has one relationship between two collection

I am using the two collection which are State and City and want to relationship between both of them and using the below code for these:
common\models\State.php
return $this->hasMany(City::className(), ['state_id'=>'_id']);
common\models\City.php
return $this->hasOne(State::className(), ['_id' =>'state_id']);
When i run the below code, it is running and return blank result for cities.
$model = \common\models\State::find()->all();
When i change the state_id type in collection with ObjectId then it returns result. So please suggest me how can i typecast the state_id in relation.
Try this...
use MongoBB namespace.
common\models\State.php
return $this->hasMany(City::className(), [new MongoDB\BSON\ObjectId(['state_id']) => '_id']);
common\models\City.php
return $this->hasOne(State::className(), ['_id' => new MongoDB\BSON\ObjectId(['state_id'])]);
For example; Comment are associated to a single user, in the comment class
public function getUser()
{
return $this->hasOne(User::className(), ['_id' => 'user_id']);
}
In a controller where do you want to retrieve the comments and user objects
$query = Comment::find()->orderBy('created_at DESC');
$dataProvider = new ActiveDataProvider([
'pagination' => ['pageSize' =>5],
'query' => $query,
]);
return $this->render('index', [
'dataProvider' => $dataProvider,
]);
You can access user objects with comments from another view like this
$model->user['profile_url']

missing FROM-clause entry for table "Grupo" cakephp

hi i have aproblem in my code, I want generate a list of user but this have a group and need just a group of user.
the error say:
Error: SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "Grupo"
this is my code:
public function add()
{
$this->loadModel('SoyaProveedor');
$this->loadModel('Soya');
$this->set('oleaginosas', $this->Soya->find('list', array(
'fields'=> array('id','username'),
'conditions' => array('Grupo.categoria' => 'Soya' , 'Grupo.subcategoria' => 'Productor de Oleaginosas')
)));
if ($this->request->is('post')) {
$this->request->data['SoyaProveedor']['nombre'] = strtoupper($this->request->data['SoyaProveedor']['nombre']);
$this->request->data['SoyaProveedor']['codigo'] = strtoupper($this->request->data['SoyaProveedor']['codigo']);
if ($this->SoyaProveedor->save($this->request->data)) {
$this->Session->setFlash(__('La InformaciĆ³n fue Guardada.'));
return $this->redirect(array('action' => 'index'));
}
}
}
the sql query of the cake generate it:
SQL Query: SELECT "Soya"."id" AS "Soya__id", "Soya"."username" AS
"Soya__username" FROM "public"."users" AS "Soya" WHERE
"Grupo"."categoria" = 'Soya' AND "Grupo"."subcategoria" = 'Productor
de Oleaginosas'
You need the grupos table to be joined in the query, your query in the question has no joins. There are a number of simple solutions.
Define recursive.
Recursive is a very coarse control of what joins and queries are executed, by default find('list') has a recursive value of -1.
-1 means no joins, which is why there is no join in the resultant query. Setting it to a value of 0 adds a join to the main query for all hasOne and belongsTo associations.
Be wary of using/relying on recursive as it's very easy to generate queries with joins you don't need - and/or triggering many subsequent queries for related data (if set to a value larger than 0).
However this find call:
$data = $this->Soya->find('list', array(
'fields'=> array('Soya.id','Soya.username'),
'recursive' => 0, // added
'conditions' => array(
'Grupo.categoria' => 'Soya' ,
'Grupo.subcategoria' => 'Productor de Oleaginosas'
)
));
Should result in this query (If the Soya model has a belongsTo association to Grupo):
SELECT
"Soya"."id" AS "Soya__id",
"Soya"."username" AS "Soya__username"
FROM
"public"."users" as "Soya"
LEFT JOIN
"public"."Grupos" as "Grupo" on ("Soya"."grupo_id" = "Grupo"."id")
...
Possibly more joins
...
WHERE
"Grupo"."categoria" = 'Soya'
AND
"Grupo"."subcategoria" = 'Productor de Oleaginosas'
Or Use containable
The containable behavior allows better control of what queries are executed. Given the info in the question to use it that means:
<?php
class Soya extends AppModel {
// Assumed from information in the question
public $useTable = 'users';
public $belongsTo = array('Grupo');
// added
public $actsAs = array('Containable');
}
Will permit you to do the following in your controller:
$data = $this->Soya->find('list', array(
'fields'=> array('Soya.id','Soya.username'),
'contain' => array('Grupo'), // added
'conditions' => array(
'Grupo.categoria' => 'Soya' ,
'Grupo.subcategoria' => 'Productor de Oleaginosas'
)
));
Which will generate the following query (exactly one join):
SELECT
"Soya"."id" AS "Soya__id",
"Soya"."username" AS "Soya__username"
FROM
"public"."users" as "Soya"
LEFT JOIN
"public"."Grupos" as "Grupo" on ("Soya"."grupo_id" = "Grupo"."id")
WHERE
"Grupo"."categoria" = 'Soya'
AND
"Grupo"."subcategoria" = 'Productor de Oleaginosas'
Link your models together using associations: CakePHP Associations
Alternatively you can use custom sql-statemens using join e.g.:
$db = $this->getDataSource();
$result = $db->fetchAll(
"SELECT Soya.id AS Soya__id, Soya.username AS Soya__username FROM public.users AS Soya
join Grupo on Grupo.id = Soya.groupo_id
WHERE Grupo.categoria = ? AND Grupo.subcategoria = ?",
array('Soya', 'Productor de Oleaginosas')
);
$this->set('oleaginosas', $result);