I have a non-standard database setup to work with and i'm trying to get this test case to work in Kohana 3.2 but not having any luck. Scenario:
I have a courses model using database A
I have a members model using database B
I have a courses_members join table in database A
Model for courses
class Model_Course extends ORM {
// Select the DB
protected $_db_group = 'default';
// override primary key
protected $_primary_key = 'courseid';
// Relationship
protected $_has_many = array(
'members' => array(
'model' => 'member',
'foreign_key' => 'memberID',
'through' => 'courses_members',
),
);
}
Model for members
class Model_Member extends ORM {
// Select the DB
protected $_db_group = 'alternate';
// override primary key
protected $_primary_key = 'memberID';
// Relationship
protected $_has_many = array(
'courses' => array(
'model' => 'course',
'foreign_key' => 'courseid',
'through' => 'courses_members'
),
);
}
Now in my controller trying i'm trying to echo out some test data
$courses = ORM::factory('course')->find_all();
foreach ($courses as $course)
{
echo $course->coursename . '<br/>';
foreach ($course->members as $member)
{
echo '-' . $member->username . '<br/>';
}
echo '<hr/>';
}
but $member->username results in an empty object. Empty objects make me sad.
Ideas? Can the Kohana ORM work this way across multiple databases?
This can be easily accomplished using the LEAP ORM for Kohana. In LEAP, all you have to do is specify in your models the data source (i.e. database config group) you want to use and the ORM will handle the switching between databases because it utilizes a database connection pool. Leap will also allow you to switch between database dialects since the module has drivers for DB2, Firebird, MariaDB, MS SQL, MySQL, Oracle, PostgreSQL, and SQLite. You can download the LEAP ORM from github at https://github.com/spadefoot/kohana-orm-leap.
It's a little late for the answer, but here is the solution. Change
foreach ($course->members as $member)
{
echo '-' . $member->username . '<br/>';
}
For:
foreach ($course->members->find_all() as $member)
{
echo '-' . $member->username . '<br/>';
}
Related
I'm trying to do a LEFT JOIN in CakePHP3.
But all I get is a "is not associated"-Error.
I've two tables BORROWERS and IDENTITIES.
In SQL this is what I want:
SELECT
identities.id
FROM
identities
LEFT JOIN borrowers ON borrowers.id = identities.id
WHERE
borrowers.id IS NULL;
I guess this is what I need:
$var = $identities->find()->select(['identities.id'])->leftJoinWith('Borrowers',
function ($q) {
return $q->where(['borrowers.id' => 'identities.id']);
});
But I'm getting "Identities is not associated with Borrowers".
I also added this to my Identities Table:
$this->belongsTo('Borrowers', [
'foreignKey' => 'id'
]);
What else do I need?
Thanx!
The foreign key cannot just be 'id', that's not a correct model association. You'd need to put a 'borrower_id' field in identities, and declare it like this in the Identities model:
class Identities extends AppModel {
var $name = 'Identities';
public $belongsTo = array(
'Borrower' => array (
'className' => 'Borrower',
'foreignKey' => 'borrower_id'
)
);
Note the capitalization and singular/plural general naming conventions which your example doesn't follow in the least - ignoring those will get you some really hard to debug errors..
Yup. It was an instance of \Cake\ORM\Table, due to my not well chosen table name (Identity/Identities). I guess it's always better not to choose those obstacles, for now I renamed it to Label/Labels.
This query now works perfectly:
$var = $identities
->find('list')
->select(['labels.id'])
->from('labels')
->leftJoinWith('Borrowers')
->where(function ($q) {
return $q->isNull('borrowers.id');
});
I have a system where all tables in the MySQL database are populated with external data (synchronized with another system every 5 minutes). All tables have a column DELFLAG which is used to mark disabled entries.
So I have about 15 AR models in Yii that are linked to those tables. Whenever I make a query, I need to add something like $criteria->addCondition('DELFLAG=0'). This gets ugly if there are multiple tables present in the query, as every one of them has the flag. Also, there's a potential for error if I forget one of those conditions.
Here's how I do it now:
public function search($showtype = NULL) {
$criteria=new CDbCriteria;
if (isset($showtype)) {
$criteria->with = array(
'TSSSHOW',
'TSSSHOW.TSSSHOWTYPEITEM',
'TSSSHOW.TSSSHOWTYPEITEM.TSSSHOWTYPE'
);
$criteria->compare('TSSSHOWTYPEITEM.TSSSHOWTYPEID', $showtype);
$criteria->addCondition('TSSSHOW.DELFLAG=0');
$criteria->addCondition('TSSSHOWTYPEITEM.DELFLAG=0');
}
$exp = new CDbExpression("`TSSEVENT_START_DATETIME` > NOW()");
$criteria->addCondition($exp);
$criteria->addCondition('t.DELFLAG=0');
$criteria->together = true;
$criteria->order = 't.TSSEVENT_START_DATETIME ASC';
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination' => array(
'pageSize' => 15,
),
));
}
Is there a convenient way to include this condition into every query that includes these tables? Perhaps a special class which my models shall descend from (as opposed to the default CActiveRecord)?
I'd suggest declaring named scope for these models:
public function scopes()
{
return array(
'disabledEntry'=>array(
'condition'=>'DELFLAG=1',
),
);
}
Using the named scope: Model::model()->disabledEntry()->findAll();
You can provide scope when relaring to model in the with() statement as well: ModelA::model()->with('model:disabledEntry')->findAll();
But if you have to set this condition each time, you may set defaultScope:
public function defaultScope()
{
return array(
'condition' => 'DELFLAG=0',
);
}
Thus, this model by default would have this condition.
Update: Since you have the identically named columns in several models, YII can have some column name ambiguity troubles while building sql-query. If it is the case, use alias in scope declaration or use the following statement to set current table alias explicitly while declaring scope 'condition' => $this->getTableAlias(false, false) . '.DELFLAG=0',
You can create Your own class e.g. CustomActiveRecord extends CActiveRecord
then You should override findAll, findByPk methods with your criteria..
Other solution is to run this query 'DELETE FROM table_name WHERE DELFLAG=1' after every import..
I have two tables tbl_business and business_contacts of the following structure:
tbl_business
---
business_id (PK)
othercolumns
and
business_contacts
---
contact_id (PK)
business_id
othercolumns
The scenario is that one business row has many contacts. I am using cGridview using gii's CRUD generator and needed to display firstname and lastname from business_contacts (one of multiple possible rows in the table) for each tbl_business record.
As far as I understand, I've updated the relation function in tbl_business's model as:
'businesscontacts' => array(self::HAS_MANY,'BusinessContact','business_id','select' => 'contact_firstname, contact_lastname')
and for the same, a contact relation is defined in the business_contacts' model as:
'contactbusiness' => array(self::BELONGS_TO,'BusinessContact','business_id')
I expected that would work for pulling related records so that I can have something in the grid like, business_id, contact_firstname, contact_lastname , ... otherbusinesstablecolumns .. but I'm only getting blank values under firstname and lastname .. could someone please help me understand the error? :(
So you are trying to display a table of Businesses (tbl_business) using CGridView? And in each Business's row you want to list multiple Contacts (business_contacts)?
CGridView does not support displaying HAS_MANY relations by default. CGridView makes it easy to list which Business a Contact BELONGS_TO (i.e. you can use a column name like contactbusiness.business_id), but not all of the Contacts that are in a business.
You can do it yourself though, by customizing a CDataColumn. (Note: this will not allow you to sort and filter the column, just view. You'll have to do a lot more work in to get those working.)
First, in your Business model, add a method like this to print out all of the contacts:
public function contactsToString() {
$return = '';
foreach ($this->businesscontacts as $contact) {
$return .= $contact->contact_firstname.' '.$contact->contact_firstname.'<br />';
}
return $return;
}
(EDIT: Or do this to print out just the first contact):
public function contactsToString() {
if($firstContact = array_shift($this->businesscontacts)) {
return $firstContact->contact_firstname.' '.$firstContact->contact_firstname;
}
return '';
}
Then make a new column in your grid and fill it with this data like so:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'business-grid',
'dataProvider'=>$model->yourDataProviderFunction(),
'columns'=>
'business_id',
array(
'header'=>'Business Contacts', // give new column a header
'type'=>'HTML', // set it to manual HTML
'value'=>'$data->contactsToString()' // here is where you call the new function
),
// other columns
)); ?>
EDIT2: Yet another way of doing this, if you just want to print out ONE of a HAS_MANY relation, would be to set up a new (additional) HAS_ONE relation for the same table:
public function relations()
{
return array(
'businesscontacts' => array(self::HAS_MANY,'BusinessContact','business_id','select' => 'contact_firstname, contact_lastname') // original
'firstBusinesscontact' => array(self::HAS_ONE, 'BusinessContact', 'business_id'), // the new relation
);
}
Then, in your CGridView you can just set up a column like so:
'columns'=>array(
'firstBusinesscontact.contact_firstname',
),
Getting only the first contact could be achieved like this also:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'business-grid',
'dataProvider'=>$model->yourDataProviderFunction(),
'columns'=>
'business_id',
//....
array(
'name' => 'contacts.contact_firstname',
'value' => '$data->contacts[0]->contact_firstname', // <------------------------
'type' => 'raw'
);
//....
),
Hi I have searched the site for my question but haven't found an easy solution and I think the issue is so basic.
I'm using Api V2 so maybe there's a solution now. Here I go, this is my code:
$api_soap_url = 'http://localhost/magento/api/v2_soap?wsdl=1';
$client = new SoapClient($api_soap_url);
$session_id = $client->__soapCall('login',array($user, $pw));
$data = array($session_id);
$result = $client->__soapCall('customerCustomerList', $data);
This returns all results, I need to limit number of result so I have tried using filters and other solutions found here but no luck.
The only one I haven't tried is this one:
Control the number of results from a Magento API call
But filtering by date doesn't solve my problem and rewriting classes is a ver complex solution for such a simple need.
Thanks in advance
I'm not sure the filter can limit number of result but you can try this:
$complexFilter = array(
'complex_filter' => array(
array(
'key' => 'created_at',
'value' => array('key' => 'gt', 'value' => '2012-05-13 06:11:00')
// where created_at is greater than 2012-05-13 06:11:00
// For example: eq (equals), neq (not equals), gt (greater than), lt (less than), etc.
)
)
);
$result = $client->customerCustomerList($session, $complexFilter);
I ended up overriding app/code/core/Mage/Sales/Model/Order/Api.php, adding a "special magic" field called "collection.limit". Your mileage may vary; I have tight controls on both the Magento installation and the programs (in this case, a set of C# programs) accessing the Magento installation.
My caller simply uses the "magic field" as a key/ value pair, something like this (please test, again, I was calling from C#, so this php should be considered suspect):
$collectionLimitClause = array (
'key' => 'collection.limit',
'value' => array('key' => 'eq', 'value' => '10')
);
In my Magento installation (this part is tested, live and running), I created a Sales/Model/Order/Api.php in my local namespace and over-rode the items function. Around the 32nd or so line of that function, you'll see this:
$apiHelper = Mage::helper('api');
$filters = $apiHelper->parseFilters($filters, $this->_attributesMap['order']);
try {
foreach ($filters as $field => $value) {
$orderCollection->addFieldToFilter($field, $value);
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
Instead, I "catch" my own magic limiter with the strncmp here, with an if-else inside the foreach:
$apiHelper = Mage::helper('api');
$filters = $apiHelper->parseFilters($filters, $this->_attributesMap['order']);
try {
foreach ($filters as $field => $value) {
if( !strncmp($field,"collection.limit",16) ) {
$orderCollection->getSelect()->limit($value['eq']);
}
else {
$orderCollection->addFieldToFilter($field, $value);
}
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
I'm not overly excited by this, but, I think it's pretty safe and it works.
taxonomies
-id
-name
taxonomy_type
-taxonomy_id
-type_id
I've configured two models:
class Model_Taxonomy{
protected $_has_many = array('types'=>array());
}
class Model_Taxonomy_Type{
protected $_belongs_to = array('taxonomy' => array());
}
*Please note that taxonomy_type is not a pivot table.*
A taxonomy can have multiple types associated.
Then, what I'm trying to do is get all taxonomies that belong to a given type id.
This is would be the SQL query I would execute:
SELECT * FROM taxonomies, taxonomy_type WHERE taxonomy_type.type_id='X' AND taxonomies.id=taxonomy_type.taxonomy_id
I've tried this:
$taxonomies = ORM::factory('taxonomy')
->where('type_id','=',$type_id)
->find_all();
Obviously this doesn't work, but I can't find info about how execute this kind of queries so I have no clue.
class Model_Taxonomy{
protected $_belongs_to = array(
'types' => array(
'model' => 'Taxonomy_Type',
'foreign_key' => 'taxonomy_id'
)
);
}
class Model_Taxonomy_Type{
protected $_has_many = array(
'taxonomies' => array(
'model' => 'Taxonomy',
'foreign_key' => 'taxonomy_id'
)
);
}
And use some like that:
$type = ORM::factory('taxonomy_type')
->where('type_id', '=', $type_id)
->find();
if( ! $type->taxonomies->loaded())
{
types->taxonomies->find_all();
}
type_id column is a PK of taxonomy_type table, am I right?
So, you have one (unique) taxonomy_type record, and only one related taxonomy object (because of belongs_to relationship). Instead of your:
get all taxonomies that belong to a
given type id
it will be a
get taxonomy for a given type id