Customise zf2 join tables to single table to get arrays - sql

Hi I have a working public function that joins 3 tables to get values for 2 arrays. Now the process only has one table (table2) which contains all the colvalues required for the arrays.
I have tried everything I know to customise this so only table2 is used to get the arrays, but I just get blank results.
Can anyone point to me an example of something similar which takes values from a single table to get 2 separate arrays. I inherited this so I do not know what the thinking behind it was.
thank you in advance.
public function get2ColArray($condition)
{
$sql =new Sql($this->adapter);
$select = $sql->select();
$select->from('table1');
$select->columns(array('colval1'));
$select->join('table2', "table1.colval2 = table2.colval3", array('colval4'), 'inner');
$select->join('table3', "tabl1.colval1= table3.colval1", array('colval5'), 'left');
if(!empty($condition['in']))
foreach ($condition['in'] as $key=>$val) {
$select->where->in($key,$val);
}
if(!empty($condition['where']))
$select->where($condition['where']);
$select->order(array('table2.colval4'=>'ASC'));
$statement = $sql->prepareStatementForSqlObject($select);
$sql_result = $statement->execute();
if ($sql_result->count() > 0) {
$results = new ResultSet();
$data = $results->initialize($sql_result);
}
return $data;
}

Got it, its abit blunt but works, thanks ...
$select->from('table2');
$select->columns(array('colval1','colval_whateveryouwantaslongasitsintable2',));

Related

Getting Custom Column from IQueryable DB First Approach EF

I am working on Database First Approach in Entity Framework where I have to retrieve specific columns from the Entity.
Public IQueryable<Entity.Employees> GetEmployeeName(String FName,String LName)
{
var query = (from s in Employees
where s.firstName = FName && s.lastName = LName
select new {s.firstName, s.middleName});
return query;
}
Here return statement is throwing an error where it seems that its not matching with Employees (entity) columns. Could you please help me in sorting out this issue? Thanks in advance.
You need to use == for comparison, also you need to use dynamic type as return type since you are returning a custom anonymous type. Try this
Public IQueryable<dynamic> GetEmployeeName(String FName,String LName)
{
var query=(from s in Employees
where s.firstName==FName && s.lastName==LName
select new {s.firstName,s.middleName});
return query.AsQueryable();
}
Finally you will use it like below, keep in mind that intelisense won't work on dynamic object.
var query = GetEmployeeName("Jake", "Smith");
List<dynamic> results = query.ToList();
foreach (dynamic result in results)
{
string fristName = result.FirstName;
string lastName = result.MiddleName;
}

Split query with doctrine correctly

I have a doctrine query which e.g. look like this:
$object = $this->createQueryBuilder('object')
->leftJoin('object.element', 'element')->addSelect('element')
->leftJoin('object.element2', 'element2')->addSelect('element2')
->leftJoin('object.many', 'many')->addSelect('many')
->leftJoin('many.element3', 'element3')->addSelect('element3')
->leftJoin('many.element4', 'element4')->addSelect('element4')
->where('object.id = 1')
->getQuery()
->getSingleResult();
The real query have more joins and need a lot of memory and the db performance is not good. In native SQL I would split it and load it correctly. What I want todo is load with the first query the object and some basic joined data. This would look like this:
$object = $this->createQueryBuilder('object')
->leftJoin('object.element', 'element')->addSelect('element')
->leftJoin('object.element2', 'element2')->addSelect('element2')
->where('object.id = 1')
->getQuery()
->getSingleResult();
Now I also want to load the many, many.element3 and many.element4. In seperate query. When I just use doctrine lazy loading feature it will create a SQL query foreach but I only want this as 1 query.
I know it would be possible to set EAGER on that relation but I only want to EAGER temporarily for this query not always when somebody join the object and access it.
Did solve my problem the following way:
In my object I did add a new set function for the collection.
/**
* Set manies.
*/
public function setManies($manies)
{
// this clear and foreach is needed to keep it as ArrayCollection so doctrine dont need for the unitofwork request the db again
$this->manies->clear();
foreach ($manies as $many) {
$this->manies->add($many);
}
return $this;
}
In the repository my code look like this:
$object = $this->createQueryBuilder('object')
->leftJoin('object.element', 'element')->addSelect('element')
->leftJoin('object.element2', 'element2')->addSelect('element2')
->where('object.id = 1')
->getQuery()
->getSingleResult();
$object->setManies($this->getEntityManager()->getRepository(Many::class)->loadByObjectId(
$object->getId()
));
return $object;
here the function in the many repository
// loadByObjectId in the many repository
$manies = $this->createQueryBuilder('many')
->leftJoin('many.element3', 'element3')->addSelect('element3')
->leftJoin('many.element4', 'element4')->addSelect('element4')
->where('many.object = 1')
->getQuery()
->getResult();
With this the SQL is successfully splitted in multiple request. In my case instead of 60000 rows in 1 request, I only had 40 rows affected in 4 request which make the hydration of the object a lot faster.

SilverStripe 3 Left Join Missing argument

I have a data object related to some other data objects and I am trying to build a reporting page for them.
So far I've got the code below in my page controller to display a form where I will begin to select filtering options for the report.
However I am getting this error due to the left join:
[Warning] Missing argument 2 for SQLQuery::addLeftJoin()
It would seem that the raw2sql is outputting this when I've debugged:
\'AgeRangeData\', \'CallEvent.AgeRangeData ID=AgeRangeData.ID)\'
I'm assuming that the backslashes is what is causing the error
public function ReportingFilter(){
$DataObjectsList = $this->dbObject('DataObjects')->enumValues();
$fields = new FieldList(
new DropdownField('DataObjects', 'Data Objects', $DataObjectsList)
);
$actions = new FieldList(
new FormAction("FilterObjects", "Filter")
);
return new Form($this, "ReportingFilter", $fields, $actions);
}
public function FilterObjects($data, $form){
$data = $_REQUEST;
$query = new SQLQuery();
$object = $data['DataObjects'];
$leftJoin = Convert::raw2sql("'" . $object . "', 'CallEvent." . $object . " ID={$object}.ID)'");
$query->selectField("CallEvent.ID", "ID");
$query->setFrom('`CallEvent`');
$query->setOrderBy('CallEvent.Created DESC');
$query->addLeftJoin($leftJoin);
return $query;
}
SQLQuery::addLeftJoin() takes two arguments. The first is the table to join on and the second is the "on" clause.
You want:
$query = new SQLQuery();
$query->addLeftJoin($object, '"CallEvent"."ID" = "' . $object . '"ID"');
You'd need to escape $object appropriately, of course.
NB: Your code looks a little fragile as you're not ensuring that you $object actually has a DB table. I recommend you use ClassInfo::baseDataClass($object). This will have the added benefit that it will also sanitise your class name and ensure it's a real class.

Two separate database queries to relate separate tables with Drupal 7 module?

I am developing a custom module for a site I'm working on and have created the following code. This is my first module, so any ideas of what I could be doing better would be appreciate.
As it is, this module works perfectly for me. But, I want to optimize it and be sure that I fix shoddy code.
Thanks!
The function in question is as follows:
// Declared variables for future incrementation
$total=0;
$countOne=0;
$countTwo=0;
$countThree=0;
$countOld=0;
// Call the native global user object from Drupal
global $user;
$userID = $user->uid;
// Check for nodes of given type owned by current user
$sql = db_query("SELECT nid FROM {node} WHERE type = 'content_type' AND uid = " . $userID);
// Iteratively checks each node id against a custom Drupal field on a separate table
foreach ($sql as $record) {
// SQL query for all custom fields attached to the node id given above
$query = db_query("SELECT * FROM {field_birth} WHERE entity_id = " . $record->nid);
$result = $query->fetchObject();
// The unmodified birth format (Y-m-d 00:00:00)
$originalBirth = $result->field_date_of_birth_value;
// The sanitized birth format for comparison (Y-m-d)
$birth = date('Y-m-d', strtotime($originalBirth));
// The current date/time (Y-m-d)
$now = date('Y-m-d');
//Future dates (Y-m-d)
$one_year = date('Y-m-d', strtotime('+1 year', strtotime($birth)));
$two_years = date('Y-m-d', strtotime('+2 years', strtotime($birth)));
$three_years = date('Y-m-d', strtotime('+3 years', strtotime($birth)));
// A count of all records returned before logical statements
$total++;
// Logic to determine the age of the records
if($now < $one_year) {
$countOne++;
}
else if($now >= $one_year && $now < $two_years) {
$countTwo++;
}
else if($now >= $two_years && $now < $three_years) {
$countThree++;
}
else {
$countOld++;
}
My question is, can I avoid having two separate database queries to hit both tables? I am not really sure how to go about that. Also, am I doing things in a manner which will be resource intensive and highly inefficient? As I am not a programmer by trade, I am not certain when code is 'good'. I do want to try my best to make this good code though since it is a module for a website I hope will last a long time.
Thank you stackoverflow community!
EDIT: The code I got working thanks to Mike is as follows. If anyone has a similar question / problem hopefully this will help!
// Join field_birth_table to nodes of given type owned by current user
$sql = db_select('node', 'n');
$sql->join('field_birth_table', 'b', 'n.nid = b.entity_id');
$sql
->fields('b', array('field_birth_field_value', 'entity_id'))
->condition('n.type', 'content_type')
->condition('n.status', '1')
->condition('n.uid', $user->uid)
->addTag('node_access');
$results = $sql->execute();
You can use a left join between the node and field_birth table:
$query = db_select('node', 'n');
$query->leftJoin('field_birth', 'b', '(b.entity_id = n.nid AND b.entity_type = :node)', array(':node' => 'node'));
$query
->fields('b', array())
->condition('n.type', 'content_type')
->condition('n.uid', $user->uid)
$results = $query->execute();

SQL to Magento model understanding

Understanding Magento Models by reference of SQL:
select * from user_devices where user_id = 1
select * from user_devices where device_id = 3
How could I perform the same using my magento models? getModel("module/userdevice")
Also, how can I find the number of rows for each query
Following questions have been answered in this thread.
How to perform a where clause ?
How to retrieve the size of the result set ?
How to retrieve the first item in the result set ?
How to paginate the result set ? (limit)
How to name the model ?
You are referring to Collections
Some references for you:
http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-5-magento-models-and-orm-basics
http://alanstorm.com/magento_collections
http://www.magentocommerce.com/wiki/1_-_installation_and_configuration/using_collections_in_magento
lib/varien/data/collection/db.php and lib/varien/data/collection.php
So, assuming your module is set up correctly, you would use a collection to retrieve multiple objects of your model type.
Syntax for this is:
$yourCollection = Mage::getModel('module/userdevice')->getCollection()
Magento has provided some great features for developers to use with collections. So your example above is very simple to achieve:
$yourCollection = Mage::getModel('module/userdevice')->getCollection()
->addFieldToFilter('user_id', 1)
->addFieldToFilter('device_id', 3);
You can get the number of objects returned:
$yourCollection->count() or simply count($yourCollection)
EDIT
To answer the question posed in the comment: "what If I do not require a collection but rather just a particular object"
This depends if you still require both conditions in the original question to be satisfied or if you know the id of the object you wish to load.
If you know the id of the object then simply:
Mage::getModel('module/userdevice')->load($objectId);
but if you wish to still load based on the two attributes:
user_id = 1
device_id = 3
then you would still use a collection but simply return the first object (assuming that only one object could only ever satisfy both conditions).
For reuse, wrap this logic in a method and place in your model:
public function loadByUserDevice($userId, $deviceId)
{
$collection = $this->getResourceCollection()
->addFieldToFilter('user_id', $userId)
->addFieldToFilter('device_id', $deviceId)
->setCurPage(1)
->setPageSize(1)
;
foreach ($collection as $obj) {
return $obj;
}
return false;
}
You would call this as follows:
$userId = 1;
$deviceId = 3;
Mage::getModel('module/userdevice')->loadByUserDevice($userId, $deviceId);
NOTE:
You could shorten the loadByUserDevice to the following, though you would not get the benefit of the false return value should no object be found:
public function loadByUserDevice($userId, $deviceId)
{
$collection = $this->getResourceCollection()
->addFieldToFilter('user_id', $userId)
->addFieldToFilter('device_id', $deviceId)
;
return $collection->getFirstItem();
}