SilverStripe duplicate entries even with unique index - sql

I'm trying to prevent duplicate records when adding customer records in my CRM with the following index:
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"FirstName","Surname"'
)
);
Note that I extended Customer from Member where FirstName and Surname came from:
class Customer extends Member
But SilverStripe is still allowing duplicate entries of FirstName and Surname combination? Has anyone experienced the same problem?

The Man, in my experience a validate() is still needed even when using indexing:
public function validate() {
$result = parent::validate();
if(Member::get()->filter(array('FirstName' => $this->FirstName, 'Surname' => $this->Surname))->first()) {
$result->error('First and Surname must be unique for each member.');
}
return $result;
}
Alternately for a more robust breakout:
public function validate() {
$result = parent::validate();
if($member = Member::get()->filter(array('FirstName' => $this->FirstName, 'Surname' => $this->Surname))->first()) {
if($member->FirstName == $this->FirstName){
$result->error('Your Surname is fine, please change your First Name.');
}
if($member->Surname == $this->Surname){
$result->error('Your First Name is fine, please change your Surname.');
}
}
return $result;
}

note that I extended Customer from Member were FirstName and Surname came from
I wonder if SilverStripe is attempting to set indexes on the non-existent fields Customer.FirstName and Customer.Surname. Maybe try qualifying the columns by prepending the table that is actually having the indexes added to it like this:
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"Member"."FirstName","Member"."Surname"'
)
);
You might also consider decorating Member instead of subclassing it. That way you wouldn't need to qualify the query fragments in this way.

The way to extend Member on SilverStripe is by extending DataExtension. As theruss is saying, you are trying to create a unique index on the table Customer, where you prabably do not have the fields FirstName and Surname.
Try this instead
class Customer extends DataExtension
{
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"FirstName","Surname"'
)
);
}
And then let SilverStripe know about your extension in config.yml
Member:
extensions:
- Customer
Now run /dev/build?flush and you should see your index being created.
Check here for more information about extensions.

Related

Left Join CakePHP3

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');
});

yii showing data on cgridview from others table

I'm having problem to show data on cgridview using foreign keys.
This is my case, i have table employee(id, username), client(id, username), and transaction(id, employeeId, clientId). employeeId foreign key to employee.id, and clientId is foreign key to client.id. Now, I want to show employee's name and client's name instead of their id on transaction admin.php.
This is my code:
class Transaction extends CActiveRecord
{
public $client_search;
public $employee_search;
public function rules()
{
return array(
.
.
.
array('id, employeeId, clientId, balance, status, date, client_search, employee_search', 'safe', 'on'=>'search'),
);
}
public function relations()
{
return array(
'employee' => array(self::BELONGS_TO, 'Employee', 'employeeId'),
'client' => array(self::BELONGS_TO, 'Client', 'clientId'),
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->with = array( 'client', 'employee' );
$criteria->together = true;
$criteria->compare('t.id',$this->id,true);
$criteria->compare('employee.username', $this->employee_search, true );
$criteria->compare('client.username', $this->client_search, true );
$criteria->compare('t.balance',$this->balance,true);
$criteria->compare('t.status',$this->status);
$criteria->compare('t.date',$this->date,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
//the other functions are there, i don't edit it.
}
that is my model/Transaction.php
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'transaction-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
array(
'header' => 'Employee',
'name' => 'employee_search',
'value' => '$data->employee->username',
),
array(
'header' => 'Client',
'name' => 'client_search',
'value' => '$data->client->username',
),
array(
'class'=>'CButtonColumn',
),
),
));
that is my views/transaction/admin.php.
and this code gave me error Trying to get property of non-object ($data->employee->id marked).
Actually I have succed to show the employee's name instead of employee's id, but, after that I use the same method for the client, and the error appear.
anyone can help me? My method is making public employee_search, add the rules, add the relation, adding $creiteria->with, then change the admin.php. Anyone please help me.
//UPDATE
SOLVED. Its actually my fault. There is an error in the database about the relation (foreign key). My coding is fine.
It's hard to guess without being able to debug your code but here are some things that could help (I'll add more as I can think of them).
It might be because both the default joinType for relations is LEFT OUTER JOIN and maybe you have a Transaction where one of them is null? If you try to access attributes on null object (as opposed to an actual ActiveRecord object), that's precisely the error message you would be seeing.
You could change it by doing:
public function relations()
{
return array(
'employee' => array(self::BELONGS_TO, 'Employee', 'employeeId',array('joinType'=>'INNER JOIN')),
'client' => array(self::BELONGS_TO, 'Client', 'clientId',array('joinType'=>'INNER JOIN')),
);
}
P.S.: INNER JOIN is the same as just JOIN
Not sure if it will help you, but it's worth trying.
More information at: http://www.yiiframework.com/doc/api/1.1/CActiveRecord#relations-detail

Yii order by another table with has_many relation

I have this Object model with 'has_many' relation to Variables
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'variable_id')
);
}
Now i want to use the variables to order my data like
$criteria->with = array('variables');
$criteria->order = 'variables.id DESC';
But it doesn't work.
Is there a way to do something like this? Thanks.
You can define the relation directly with an order if you want, in this case you can do.
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'object_id', 'order'=>'variables.id DESC')
);
}
What you wrote it is not working because you have a 1 to many relation. The criteria will run 2 queries, 1 to get the main record, the second time to get the relations. That is why your order is not working.
If you want it to work like you said you should do a ->join instead of ->with.
There is quite a difference between the 2 so take care how you are writing the criteria.
I think the issue is with foreign key binding, if you are adding a relationship in object table (Object model) which has many variables having object_id as foreign key in variable table (Variable model) then you need to define relationship as follows:
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'object_id') // check the change in foreign key column
);
}

CGridview and Yii Active Record Relation

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'
);
//....
),

Kohana ORM: Get results based on value of a foreign table

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