What I'm wondering is:
is it possible in Yii to add some kind of property in a Model, so only items with the property isdeleted set as 0 are shown?
So I'm looking for a way, Yii would just ignore these instances of the items...
Something like:
public function rules()
{
return array(
...
array('isdeleted', 'shouldEqualTo=>0'),
...
);
}
I thought messing around with rules() would be a way, but it doesn't work or I am doing it wrong...
You should use scopes() for that.
public function scopes()
{
return array('active' => array('condition' => 'isdeleted = 0'));
}
Then
$active = MyModel::model()->active()->findAll();
EDIT:
If you want to make the filter default, implement defaultScope() function:
public function defaultScope()
{
return array('condition' => 'isdeleted = 0');
}
Thanks to W.B.'s answer I knew to look into scopes, you can use scopes like W.B. did:
public function scopes()
{
return array('active' => array('condition' => 'isdeleted = 0'));
}
and then use
$active = MyModel::model()->active()->findAll();
If you do not want to change your code in your project (like me) you can use:
public function defaultScope()
{
return array(
'condition' => 'isdeleted = 0',
);
}
and then use
$active = MyModel::model()->findAll();
Related
AR model Player:
public function scopes()
{
return array(
'proleague' => array(
'condition' => 'mode = "proleague"',
),
'main' => array(
'condition' => 'mode = "main"',
),
);
}
Using model Player:
Player::model()->
proleague()->
with('startposition')->
findAllByAttributes(... here some condition ...);
^^^ That's all ok. Scope-condition will be executed. But...
In my project I have many places where any scope for Player model doesn't specified and in this cases I need use this scope-condition as default:
'main' => array(
'condition' => 'mode = "main"',
)
If I add defaultScope() method to Player model like this
public function defaultScope()
{
return array(
'condition' => 'mode = "main"',
);
}
the next code
Player::model()->
proleague()->
with('startposition')->
findAllByAttributes(... here some condition ...);
won't run correct. I won't get mode = "proleague" condition, becouse I'll use defaultScope() with mode = "main".
Any suggestions? How can I resolve the problem?
You should just use the resetScope(true) method. It "removes" the defaultScope filter.
$model = Player::model()->resetScope(true)->proleague();
create a new Class for this.
<?php
## e.g. protected/models/
class MyCoreAR extends CActiveRecord
{
/**
* Switch off the default scope
*/
private $_defaultScopeDisabled = false; // Flag - whether defaultScope is disabled or not
public function setDefaultScopeDisabled($bool)
{
$this->_defaultScopeDisabled = $bool;
}
public function getDefaultScopeDisabled()
{
return $this->_defaultScopeDisabled;
}
public function noScope()
{
$obj = clone $this;
$obj->setDefaultScopeDisabled(true);
return $obj;
}
// see http://www.yiiframework.com/wiki/462/yii-for-beginners-2/#hh16
public function resetScope($bool = true)
{
$this->setDefaultScopeDisabled(true);
return parent::resetScope($bool);
}
public function defaultScope()
{
if(!$this->getDefaultScopeDisabled()) {
return array(
'condition' => 'mode = "main"',
);
} else {
return array();
}
}
}
In your code:
// no default scope
$model = Player::model()->noScope()->proleague();
// with default scope
$model = Player::model()->proleague();
I would like to make a Yii Model rule dynamic, according to an attribute.
It's not giving error but also not working.
Am I doing something wrong? There any easier way to do it?
Model.php (Attributes: NAME, TYPE)
public function rules()
{
return array(
// Name is only required when Type is equal 1.
$this->type==1 ? array('name', 'required') : null,
);
}
Change to ( use scenario):
public function rules()
{
return array(
array('name', 'required','on'=>'typeTrue')
);
}
And in controller
public function actionSome() {
$model = new Model();
if ( $model->type == 1 ) {
$model->setScenario('typeTrue');
}
}
I found the answer and I would like to share.
For it need to use Yii rules scenarios.
Model.php:
public function rules()
{
return array(
array('name', 'required', 'on'=>'type1'),
);
}
Controller.php:
...
if ($model->type==1) {
$model->scenario = 'type1';
}
....
class AppointmentsController extends AppController {
public function view($id = null) {
if (!$this->Appointment->exists($id)) {
throw new NotFoundException(__('Invalid appointment'));
}
$options = array('conditions' => array('Appointment.' . $this->Appointment->primaryKey => $id));
$this->set('appointment', $this->Appointment->find('first', $options));
$kk = $this->Appointment->find('first', array('fields' => 'status', 'conditions' => array('Appointment.id' => $id)));
$ss = reset($kk);
$stats = reset($ss);
}
}
I have set $stats via getting value from DB and i want to use in in another function in same controller
then I want to use like this
class AppointmentsController extends AppController {
function confirm(){
$stats = 'New';
}
}
Seeing that it's the same class, have you tried using $this->stats instead of a local variable?
You can call another function in your first function like
$this->confirm($status); // calling confirm function
function confirm($status)
{
//use status as you want
}
Or you can set status as global variable then this can you accessible in any function.
I think, you've to create function in the model that will return the value of the field by id.
<?php
// model/AppModel.php
public function getFieldById($field='id', $id){
return $this->field($field, array('id' => $id));
}
// in controller function you can access this like.
$status = $this->Appointment->getFieldById('status', $id); // $id will be appointment id.
?>
I have a table Friend (PersonA, PersonB). These are foreign keys of Person(id, name).
I want to create a Yii relation between them. This is what I have come up with:
public function relations() {
return array(
'friends1' => array(self::HAS_MANY, 'Friend', 'PersonA'),
'friends2' => array(self::HAS_MANY, 'Friend', 'PersonB'),
);
}
Is there a way to combine these two relations into one? I was hoping for something like this:
public function relations() {
return array(
'allFriends' => array(self::HAS_MANY, 'Friend', 'PersonA, PersonB'),
);
}
Any ideas?
EDIT #1:
For completeness, let's also imagine that I want to order friends1 and friends2 like this:
public function relations() {
return array(
'friends1' => array(self::HAS_MANY, 'Friend', 'PersonA', 'order'=>'id ASC'),
'friends2' => array(self::HAS_MANY, 'Friend', 'PersonB', 'order'=>'id ASC'),
);
}
This solution worked for me :
Override the __get function in your model:
public function __get($name)
{
if(($name == 'friends1') || ($name == 'friends2')) {
return parent::__get('friends1') + parent::__get('friends2');
}
else
return parent::__get($name);
}
I had the exact same thing come up in something I was working on. What I did is I created another function to compile the two relations into one array. The reason for this is because even if you were to get the two relationships combined, each time you would still need to test to see if PersonA or PersonB is the current user's id and use the other id to pull the Friends info. Here is the function I created in my model:
public function getFriends() {
$all_friends = array();
foreach($this->friends1 as $friend) {
if($friend->PersonA == $this->id) {
$all_friends[] = $friend->PersonA;
} else {
$all_friends[] = $friend->PersonB;
}
}
foreach($this->friends2 as $friend) {
if($friend->PersonA != $this->id) {
$all_friends[] = $friend->PersonA;
} else {
$all_friends[] = $friend->PersonB;
}
}
return $all_friends;
}
Another options:
public function getFriends() {
$criteria = new CDbCriteria;
$criteria->compare('PersonA',$this->id);
$criteria2 = new CDbCriteria;
$criteria2->compare('PersonB',$this->id);
$criteria->mergeWith($criteria2,'OR');
$friends = Challenge::model()->findAll($criteria);
return $friends;
}
Then for any Person you can just say:
$person = Person::model()->findByPk(1);
$friends = $person->friends;
Or instead of an array you could send back an CActiveDataProvider that you could do sorts and other things with. You could do this:
public function getFriends() {
$criteria = new CDbCriteria;
$criteria->compare('PersonA',$this->id);
$criteria2 = new CDbCriteria;
$criteria2->compare('PersonB',$this->id);
$criteria->mergeWith($criteria2,'OR');
return new CActiveDataProvider('Friend', array(
'criteria' => $criteria,
));
}
Then since you have CActiveDataProvider you can sort:
$friends = $user->friends->getData(); //not sorted or anything
//or you can manipulate the CActiveDataprovider
$data = $user->friends;
$data->setSort(array(
'defaultOrder'=>'PersonA ASC',
));
$friends = $data->getData();
I have multiple objects that I'd like to be accessible via human-readable URLs, so I am generating aliases (e.g. blog.com/this-is-an-alias/) for all database records.
What is the best practice for generating these automatically?
I'm currently hook the model's 'values()' method and producing a new alias in there (based on the required 'name' field), but I can't help but feel this could done more elegantly using, say, Kohana's built-in filters.
Here's the stripped-down model:
class Model_Category extends ORM {
// relevant rules:
public function rules(){
return array(
'alias' => array(
array('max_length', array(':value', 63)),
array(array($this, 'unique'), array(':field', ':value')),
),
'name' => array(
array('max_length', array(':value', 63)),
),
// (...)
);
}
// overrides default method:
public function values(array $values, array $expected = NULL){
if(!$this->_loaded){
if($values['name'] && !$values['alias'])
$values['alias'] = Helper_Form::to_alias($values['name']);
}
return parent::values($values, $expected);
}
}
FYI, the to_alias function simply looks like this:
return strtolower(substr(trim(preg_replace('/[^0-9a-zA-Z]+/','-',$str),'-'),0,63));
So, my questions:
is it possible and/or advisable to access a model's property--other than the one being filtered--from within a filter callback?
(so far my tests seem to suggest no, or, at best, unreliably)
more importantly, how can I build-in a uniqueness test? i.e., after auto-generating an alias, how can I be sure that slug isn't already in use without throwing the model's rules() exception?
I would do that automatic generation using filters as you mentioned. Below is my conception of doing this. For filter function you can pass some specific parameters like :model, :value, :field and in this case I'd use :model to get access to current model's properties.
class Model_Category extends ORM {
public function filters()
{
return array(
'slug' => array(
array(array($this, 'create_slug'), array(':model')),
),
);
}
public function create_slug($model)
{
$model = ($model instanceof ORM) ? $model : $this;
$number = 1;
$slug = UTF8::transliterate_to_ascii($model->name);
$slug = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $slug);
$slug = UTF8::strtolower(trim($slug, '-'));
$slug = preg_replace("/[\/_|+ -]+/", '-', $slug);
while ( ! $this->unique_slug($slug))
{
if ( ! preg_match('/\d+$/', $slug))
{
$slug .= '-'.$number;
}
$slug = preg_replace('/\d+$/', $number++, $slug);
}
return $slug;
}
public function unique_slug($slug)
{
return (bool) DB::select(array('COUNT("*")', 'total_count'))
->from($this->_table_name)
->where('slug', '=', $slug)
->where($this->_primary_key, '!=', $this->pk())
->execute($this->_db)
->get('total_count');
}
}
Above example may be not fully functional but I hope you get the idea.