问题
I have an model with a relation, and I want to instantiate a new object of the relations type.
Example: A person has a company, and I have a person-object: now I want to create a company-object.
The class of the companyobject is defined in the relation, so I don't think I should need to 'know' that class, but I should be able to ask the person-object to provide me with a new instance of type company? But I don't know how.
This is -I think- the same question as New model object through an association , but I'm using PHPActiveRecord, and not the ruby one.
Reason behind this: I have an abstract superclass
person
, and two children have their own relation with a type of company object. I need to be able to instantiate the correct class in the abstract person.
A workaround is to get it directly from the static $has_one
array:
$class = $this::$has_one[1]['class_name'];
$company = new $class;
the hardcoded number can of course be eliminated by searching for the association-name in the array, but that's still quite ugly.
If there is anyone who knows how this is implemented in Ruby, and how the phpactiverecord implementation differs, I might get some Ideas from there?
Some testing has revealed that although the "search my classname in an array" looks kinda weird, it does not have any impact on performance, and in use it is functional enough.
回答1:
You can also use build_association()
in the relationship classes.
Simplest way to use it is through the Model's __call, i.e. if your relation is something like $person->company
, then you could instantiate the company with $company = $person->build_company()
Note that this will NOT also make the "connection" between your objects ($person->company
will not be set).
Alternatively, instead of build_company()
, you can use create_company()
, which will save a new record and link it to $person
回答2:
In PHPActiveRecord, you have access to the relations array. The relation should have a name an you NEED TO KNOW THE NAME OF THE RELATIONSHIP/ASSOCIATION YOU WANT. It doesn't need to be the classname, but the classname of the Model you're relating to should be explicitly indicated in the relation. Just a basic example without error checking or gritty relationship db details like linking table or foreign key column name:
class Person extends ActiveRecord\Model {
static $belongs_to = array(
array('company',
'class_name' => 'SomeCompanyClass')
);
//general function get a classname from a relationship
public static function getClassNameFromRelationship($relationshipName)
foreach(self::$belongs_to as $relationship){
//the first element in all relationships is it's name
if($relationship[0] == $relationshipName){
$className = null;
if(isset($relationship['class_name'])){
$className = $relationship['class_name'];
}else{
// if no classname specified explicitly,
// assume the clasename is the relationship name
// with first letter capitalized
$className = ucfirst($relationship);
}
return $className
}
}
return null;
}
}
To with this function, if you have a person object and want an object defined by the 'company' relationship use:
$className = $person::getClassNameFromRelationship('company');
$company = new $className();
回答3:
I'm currently using below solution. It's an actual solution, instead of the$has_one[1]
hack I mentioned in the question. If there is a method in phpactiverecord I'm going to feel very silly exposing msyelf. But please, prove me silly so I don't need to use this solution :DI am silly. Below functionality is implemented by the create_associationname call, as answered by @Bogdan_D
Two functions are added. You should probably add them in the
\ActiveRecord\Model
class. In my case there is a class between our classes and that model that contains extra functionality like this, so I put it there.
These are the 2 functions:
public function findClassByAssociation($associationName)
- Called with the name of the association you are looking for.
- Checks three static vars (
has_many
,belongs_to
andhas_one
) for the association - calls
findClassFromArray
if an association is found. - from the person/company example:
$person->findClassByAssociation('company');
private function findClassFromArray($associationName,$associationArray)
- Just a worker-function that tries to match the name.
Source:
/**
* Find the classname of an explicitly defined
* association (has_one, has_many, belongs_to).
* Unsure if this works for standard associations
* without specific mention of the class_name, but I suppose it doesn't!
* @todo Check if works without an explicitly set 'class_name', if not: is this even possible (namespacing?)
* @todo Support for 'through' associations.
* @param String $associationName the association you want to find the class for
* @return mixed String|false if an association is found, return the class name (with namespace!), else return false
* @see findClassFromArray
*/
public function findClassByAssociation($associationName){
//$class = $this::$has_one[1]['class_name'];
$that = get_called_class();
if(isset($that::$has_many)){
$cl = $this->findClassFromArray($associationName,$that::$has_many);
if($cl){return $cl;}
}
if(isset($that::$belongs_to)){
$cl = $this->findClassFromArray($associationName,$that::$belongs_to);
if($cl){return $cl;}
}
if(isset($that::$has_one)){
$cl = $this->findClassFromArray($associationName,$that::$has_one);
if($cl){return $cl;}
}
return false;
}
/**
* Find a class in a php-activerecord "association-array". It probably should have a specifically defined class name!
* @todo check if works without explicitly set 'class_name', and if not find it like standard
* @param String $associationName
* @param Array[] $associationArray phpactiverecord array with associations (like has_many)
* @return mixed String|false if an association is found, return the class name, else return false
* @see findClassFromArray
*/
private function findClassFromArray($associationName,$associationArray){
if(is_array($associationArray)){
foreach($associationArray as $association){
if($association['0'] === $associationName){
return $association['class_name'];
}
}
}
return false;
}
来源:https://stackoverflow.com/questions/9280455/activerecord-association-create-new-object-find-class