活动记录关联:创建新对象(查找类)


Activerecord-association: create new object (find class)

我有一个带有关系的模型,我想实例化一个关系类型的新对象。

示例:一个人有一家公司,而我有一个人对象:现在我想要创建公司对象。

companyobject的类是在关系中定义的,所以我认为我不需要"知道"那个类,但我应该能够要求person对象为我提供类型company的新实例?但我不知道怎么做。

我认为,这与通过关联的New模型对象的问题相同,但我使用的是PHPActiveRecord,而不是ruby。


背后的原因:我有一个抽象的超类person,两个孩子与一种类型的公司对象有自己的关系。我需要能够在抽象人中实例化正确的类。

一种解决方法是直接从static $has_one阵列获取:

$class   = $this::$has_one[1]['class_name'];
$company = new $class;

当然,可以通过在数组中搜索关联名称来消除硬编码的数字,但这仍然很难看。


如果有人知道这是如何在Ruby中实现的,以及phpactiverecord的实现有何不同,我可能会从中得到一些想法?


一些测试表明,尽管"在数组中搜索我的类名"看起来有点奇怪,但它对性能没有任何影响,而且在使用中它足够实用。

您也可以在关系类中使用build_association()
使用它的最简单方法是通过Model的__call,即如果您的关系类似于$person->company,则可以使用
$company = $person->build_company()

实例化公司请注意,这不会在对象之间建立"连接"(不会设置$person->company
或者,您可以使用create_company()而不是build_company(),它将保存新记录并将其链接到$person

在PHPActiveRecord中,您可以访问关系数组。关系应该有一个名字,你需要知道你想要的关系/协会的名字。它不需要是类名,但与之相关的模型的类名应该在关系中明确指示。这只是一个没有错误检查或粗糙关系的基本示例数据库详细信息,如链接表或外键列名:

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

使用此功能,如果您有一个个人对象,并且想要一个由"公司"关系定义的对象,请使用:

$className = $person::getClassNameFromRelationship('company');
$company = new $className();

我目前正在使用以下解决方案。相反,这是一个实际的解决方案我在问题中提到的$has_one[1]黑客。如果有phpactiverecord中的方法我会觉得很傻msyelf。但请证明我很傻,这样我就不需要用这个了解决方案:D

我很傻。以下功能由create_associationname调用实现,由@Bogdan_D 应答


增加了两个功能。您可能应该将它们添加到'ActiveRecord'Model类中。在我的例子中,在我们的类和那个模型之间有一个类,它包含这样的额外功能,所以我把它放在那里。

这是两个功能:

  • public function findClassByAssociation($associationName)
    • 使用您要查找的关联的名称调用
    • 检查关联的三个静态变量(has_manybelongs_tohas_one
    • 如果找到关联,则调用CCD_ 15
    • 来自个人/公司示例:$person->findClassByAssociation('company');
  • private function findClassFromArray($associationName,$associationArray)
    • 只是一个试图匹配名称的辅助函数

来源:

/**
 * 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;
}