The following code makes a ModelAdmin for MyDataObject. When adding a MyDataObject the edit form is empty because many_many relationships can only be seen after saving.
class TestAdmin extends ModelAdmin {
static $managed_models = array('MyDataObject');
static $url_segment = 'testadmin';
static $menu_title = 'TestAdmin';
}
class MyDataObject extends DataObject {
private static $many_many= array('MyOtherDataObjects' => 'MyOtherDataObject');
}
class MyOtherDataObject extends DataObject {
private static $db = array('Name' => 'Varchar(255)');
private static $belongs_many_many = array('MyDataObjects' => 'MyDataObject');
}
Is there any method of having the many_many relationships exist right away?
If not is there a way of creating the object and then removing it if no relationships are saved?
If I understood correctly, your Package can have many filters and one Filter can be used by different Package. Then you have many-many relation between Package and Filter. The list of the products is dynamic, you can calculate it every time.
Your users should refer to the Package somehow, so it should have a property Name and you have at least one input box to add new Package (not the blank screen as you have now).
Update
The behaviour you have described is correct and expected.The ManyManyList state is synchronised with database, so you cannot have items stored just in memory until you decide to store the list in the DB out of the box.
You could substitute the Add New button with your own implementation to automatically create new object, save it in the DB and redirect to edit screen.
Related
How to Enable Filter functionality in Security area Silverstripe Admin?
Please tell me solution.
here i want to add filter functionality like second screenshot
like this
Well, SecurityAdmin for some reason doesn't subclass ModelAdmin but LeftAndMain directly. Depending on what you want to do it may be easiest to make your own MemberAdmin as a subclass of ModelAdmin. This has a search form and takes Member's $searchable_fields array into account.
class MemberAdmin extends ModelAdmin {
private static $url_segment = 'members';
private static $managed_models = array(
'Member'
);
}
would be a simple start for that. You can make a DataExtension for Member and edit the searchable fields defining a method called updateSearchableFields() or defining directly as the private static $searchable_fields array.
Add this in your Company class:
static $searchable_fields = array(
'Title',
'State',
'Description',
);
I have one table in the database(mysql). But this table stores several slightly different types of rows. The type depends on this tables's type column. I have an abstract ActiveRecord class for a table and several descendant subclasses implementing slightly different logic for the rows of the same table of different types. Now I am implementing an update controller action for all the types of rows. I am provided with an id of the row and need to create an ActiveRecord instance representing the row with this id. But I somehow need to create instances of different subclasses depending on the type of the corresponding row.
If I were provided with both a type and an id I could've used a factory to pick a corresponding subclass. But I can already have the type in the database and an id gives me enough information to pick it from there. But if I were to pick the type from the database first and then to create an instance of the corresponding subclass that would've meant executing the same query twice.
I want to find a good way to get the data from the database and then pick a right ActiveRecord subclass to create an instance for it without making excessive queries or requiring excessive data. Is there a way to do it Yii2?
Or should I approach this problem somehow differently? The actual problem is having several almost the same but a bit different entities stored in a single table with a bit different business-logic.
One of approaches to this problem is called "Single table inheritance" and described by Martin Fowler here. There is also good article about its implementation in Yii 2 in samdark's (one of the main Yii 2 contributors) cookbook, which is currently in process of writing but is available on Github.
I'm not going to copy the whole article, but leaving just link is also not enough. Here are some important things:
1) Create common query for all types of objects (for example cars):
namespace app\models;
use yii\db\ActiveQuery;
class CarQuery extends ActiveQuery {
public $type;
public function prepare($builder)
{
if ($this->type !== null) {
$this->andWhere(['type' => $this->type]);
}
return parent::prepare($builder);
}
}
2) Create separate model for each type (extending from common model Car):
Sport cars:
namespace app\models;
class SportCar extends Car
{
const TYPE = 'sport';
public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}
public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}
Heavy cars:
namespace app\models;
class HeavyCar extends Car
{
const TYPE = 'heavy';
public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}
public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}
3) Override instantiate() method in Car model to return correct type of cars:
public static function instantiate($row)
{
switch ($row['type']) {
case SportCar::TYPE:
return new SportCar();
case HeavyCar::TYPE:
return new HeavyCar();
default:
return new self;
}
}
Then you can use any type of cars individually as regular models.
I'm currently working on a content management system in Laravel 4.
The current situation is that a Page class has many Item classes. Plain and simple.
As time passed, the Item class grew in size where we implemented new features or data an Item could store. At this point, an Item can contain information for -containers, plain text, images, navigation, besides "shared" data that every item always has, eg. position, is_visible, etc.
Now my belief is that the current state is unmanagable, so i started thinking of deviding the Item class into multiple "inheriting" classes (as a C# developers background), like this:
Item.php
id
position
isVisible
isCommentable
Container.php inherits Item.php
bgColor
bgImage
size
Text.php inherits Item.php
header
content
(So again, all this from a C# point of view).
Now, since I'm working in Laravel, having the Item class inheriting from Eloquent, I thought the proper solution is to bind Container/Text classes to the Item class by morphMany and morphTo functionallity. The wall I'm hitting is this one: should I make choose to "create" a Text object: how would I ensure that the "base data" in the Item-class gets filled aswell.
When setting up the system, I'm working with migrations to fill the first records.
Text.php
class Text extends Eloquent {
protected $fillable = [];
public function items()
{
return $this->morphMany('Item', 'itemable');
}}
Item.php
class Item extends Eloquent
{
protected $table = "items";
public function page()
{
return $this->belongsTo('Page');
}
public function itemable()
{
return $this->morphTo();
}
}
I will spare you the table setup migrations, it's the insert that i'm interested in:
public function up()
{
DB::table('containers')->insert(array(
'bgColor'=>'#ffebcf',
'bgImage'=>'img/header-jumbotron.png',
'bgImageRepeat'=>'repeat',
'bgPosition'=>'left top',
));
}
When executing this, offcourse, it won't have any information for the initial Item object. But this should be required because without this information, the Container object shouldn't excist at all. Maybe my thinking is a little weird, or I'm missing something that is very very obvious. I'm looking for a neat solution that fits my 'thoughtproces' - can't find it! At this time: the wall has struck.
Also - I would like some feedback as to my approach. Is this the right thing to do in the first place? Appreciated!
i'm working with Agile Toolkit
i got a Model_Product
class Model_Product extends Model_Table {
public $table="product";
function init(){
parent::init();
$this->addField('name')->mandatory(true);
$this->addField('price')->mandatory(true)->type('money');
$this->addField('user_id')->refModel('Model_User')
->defaultValue($this->api->auth->get('id'));
//$this->hasOne('User',null,'email'); => send me an error message
}
}
and Model_User
class Model_User extends Model_Table {
public $table="user";
function init(){
parent::init();
$this->addField('first_name')->mandatory('Prénom nécesssaire');
$this->addField('last_name')->mandatory('Nom nécesssaire');
$this->addField('email')->mandatory('entrez un email valide');
$this->addField('nationality')->mandatory('nécessaire')->enum(array('FR','EN','US'));
$this->addField('birthday')->defaultValue(date('Y-m-d'))->type('date');
$this->addField('is_admin')->type('boolean');
$this->hasMany('Product','user_id');
}
I want to list on a User page all the products from one User
$q=$this->api->db->dsql();
$q->table('product')->where('product.user_id',$this->api->auth->model['id']);
$tab->add('GRID')->setModel($q);
Some way, I get it wrong because I get an error no mater how I try to filter my Model.
If you're not using newest ATK4 version from Github then you should grab it and stay up-to-date.
You should do like this:
1) In Model_Product create hasOne reference and not refModel (it's deprecated).
// adding 'user_id' parameter is not needed, it'll be calculated anyway
// but many developers add it anyway to clear thing up a bit.
$this->hasOne('User','user_id')->defaultValue($this->api->auth->get('id'));
2) Model_User is OK.
Just some side-notes about it:
I don't think you should make birthday = today() by default.
It's quite unbelievable that child at his first day in this world will use computer :)
is_admin should be mandatory + defaultValue(false) - by default user is not admin.
3) How to list all all products from current user.
// model of current user
$model = $this->add('Model_User')
->load($this->api->auth->get('id'));
// add grid
$page->add('Grid')
// reference Product model with condition already set
->setModel($model->ref('Product'));
and that's it.
Maybe even better and safer way is to define new model class for logged in user:
class Model_Myself extends Model_User {
function init(){
parent::init();
$this->addCondition('id', $this->api->auth->get('id'));
$this->loadAny(); // I'm not sure do we have to explicitly load it here
}
}
and then create grid like this
// model of products of current user
$prod_model = $this->add('Model_Myself')->ref('Product');
// add grid
$page->add('Grid')->setModel($prod_model);
I'm trying to use one php object on several different types of post. Each type of post has several extra data members that the others don't have. The object will need some service from other classes that will check if( $something instanceof $object ). So,using extends may not work, and I'm trying to use one class for all. In the object class, should I list all possible data members as default and set the unused ones as null, or ignore the unused ones?
for example
class OBJ{
// common members that share among all posts:
public $id;
public $url;
public $embed_service
//post has grade
public $link_grade;
//posts has license
public $license;
//posts has corresponding topics
public $comment_topic_id;
//different types of data comes from different actions, saved in one table.
}
What you should do is using inheritance and derive different types of post from a common object:
class Post
{
protected $Content = 'enter content';
}
class Blog extends Post
{
protected $Categorie;
}
class Comment extends Post
{
protected email;
}
The result of this is:
all classes have a Content property
the blog class has a extra Category property
the comment class has a extra email property