Entity/DocumentRepository.php
author ymh <ymh.work@gmail.com>
Thu, 01 Mar 2012 18:43:05 +0100
changeset 80 cf6a88559482
parent 74 901463f9b11c
permissions -rwxr-xr-x
Correct p[roblem with getTags returning Null Unit test added.

<?php
/*
 * This file is part of the WikiTagBundle package.
 *
 * (c) IRI <http://www.iri.centrepompidou.fr/>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace IRI\Bundle\WikiTagBundle\Entity;

use IRI\Bundle\WikiTagBundle\Model\DocumentInterface;

use Doctrine\ORM\EntityRepository;
use IRI\Bundle\WikiTagBundle\Entity\Document;
use Doctrine\ORM\Query\ResultSetMapping;
use \ReflectionClass;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use IRI\Bundle\WikiTagBundle\Model\ModelException;

/**
 * DocumentRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class DocumentRepository extends EntityRepository
{
    /**
     * The cache for the host document class
     * @var ReflectionClass
     */
    private $reflection_class;
    
    /**
     * The cache for the wikitag document class
     * @var ReflectionClass
     */
    private $reflection_doc_class;
    private $set_methods = array();
    private $get_methods = array();

    /**
     * Find one wikitagRepository by its externalId i.e. the host document id.
     * @param $external_id
     */
    public function findOneByExternalId($external_id)
    {
        return $this->findOneBy(array("externalId" => $external_id));
    }
    
    
    public function reflectionSetField($object, $method_name, $value)
    {
        if(isset($this->set_methods[$method_name]))
        {
            $set_method = $this->set_methods[$method_name];
        }
        
        if(!isset($set_method) || is_null($set_method))
        {
            if(is_null($this->reflection_doc_class))
            {
                $this->reflection_doc_class = new ReflectionClass(get_class($object));
            }
            
            $set_method = null;
            if($this->reflection_doc_class->hasMethod($method_name))
            {
                $set_method = $this->reflection_doc_class->getMethod($method_name);
            }
            if(!is_null($set_method))
            {
                $this->set_methods[$method_name]=$set_method;
            }
        }
        
        if(!isset($set_method) || is_null($set_method) || !$set_method->isPublic())
        {
            throw new \Exception("setter method unknown $method_name");
        }
        
        //set value
        $set_method->invoke($object, $value);
        
    }
    
    public function reflectionGetField($document, $accessor)
    {
        
        if(!isset($this->get_methods[$accessor]) ||  is_null($this->get_methods[$accessor]))
        {
            if(is_null($this->reflection_class))
            {
                $this->reflection_class = new \ReflectionClass(get_class($document));
            }
            
            //look at properties
            if($this->reflection_class->hasProperty($accessor))
            {
                $get_object = $this->reflection_class->getProperty($accessor);
                if(!$get_object->isPublic())
                {
                    $get_object = NULL;
                }
            }
            
            if((!isset($get_object) || is_null($get_object)) && $this->reflection_class->hasMethod($accessor))
            {
                $get_object = $this->reflection_class->getMethod($accessor);
                if(!$get_object->isPublic())
                {
                    $get_object = NULL;
                }
            }
            
            if((!isset($get_object) || is_null($get_object)) && $this->reflection_class->hasMethod("get".ucfirst($accessor)))
            {
                $get_object = $this->reflection_class->getMethod("get".ucfirst($accessor));
                if(!$get_object->isPublic())
                {
                    $get_object = NULL;
                }
            }

            if(isset($get_object) && !is_null($get_object))
            {
                $this->get_methods[$accessor] = $get_object;
            }
        }

        if(isset($this->get_methods[$accessor]))
        {
            $get_object = $this->get_methods[$accessor];
            if(!is_null($get_object))
            {
                if(is_a($get_object,"\ReflectionMethod"))
                {
                    return $get_object->invoke($document);
                }
                elseif(is_a($get_object,"\ReflectionProperty"))
                {
                    return $get_object->getValue($document);
                }
                else
                {
                    throw new ModelException("Bad reflection object type");
                }
            }
        }
        
        throw new ModelException("Unknown accessor $accessor");
    }
    
    
    private function getColumnName($field_name)
    {
        if(isset($this->getClassMetadata()->columnNames[$field_name]))
        {
            return $this->getClassMetadata()->columnNames[$field_name];
        }
        
        $res = null;
        if(isset($this->getClassMetadata()->associationMappings[$field_name]))
        {
            $association_mapping = $this->getClassMetadata()->associationMappings[$field_name];
            if(
                isset($association_mapping['type'])
                && ( $association_mapping['type'] === ClassMetadataInfo::ONE_TO_ONE || $association_mapping['type'] === ClassMetadataInfo::MANY_TO_ONE)
                && count($association_mapping['joinColumns']) > 0
            )
            {
                $res = $association_mapping['joinColumns'][0]['name'];
            }
        }
        
        if(is_null($res)) {
            throw new \Exception("WikiTag.DocumentRepository: Unknown field $field_name");
        }
        return $res;
    }
    
    /**
     * Write a wikitag document given the host document and the field list.
     * @param $document The source document
     * @param $document_id_column the name of the source document id column
     * @param $fields The list of field definition. This is an associative array [<field name>=><field definition>].
     *     See the @IRI\Bundle\WikiTagBundle\DependencyInjection\Configuration documentation
     */
    public function writeDocument($document,  $document_id_column, $fields)
    {
        // get document from id
         
        $docid = $this->reflectionGetField($document, $document_id_column);
        $baseDocument = $this->findOneByExternalId($docid);
    
        if(is_null($baseDocument))
        {
            $baseDocument = new Document();
            $baseDocument->setExternalId($document);
        }
        
        foreach ($fields as $name => $field_def) {
            if(isset($field_def['accessor']))
            {
                $accessor = $field_def['accessor'];
            }
            else
            {
                $accessor = NULL;
            }
            if(is_null($accessor))
            {
                $accessor = $name;
            }
            
            $value = strval($this->reflectionGetField($document,$accessor));
            
            $method_name = "set".ucfirst($name);
            
            $this->reflectionSetField($baseDocument, $method_name, $value);
            
        }
        
        $this->getEntityManager()->persist($baseDocument);
        return $baseDocument;
    
    }
    
    /**
     * Remove a Wikitag doument given the host docuument.
     * @param $document The host document
     * @param string $document_id_column The host document id column name
     */
    public function removeDocument($document, $document_id_column)
    {
        $docid = $this->reflectionGetField($document, $document_id_column);
        $baseDocument = $this->findOneByExternalId($docid);
        if(!is_null($baseDocument))
        {
            $this->getEntityManager()->remove($baseDocument);
        }
    }
    
    
    /**
     * return the list of a wikitag documents the tags label.
     * @param DocumentInterface $document the wikitag document
     * @return array
     */
    public function getTagsStr($document)
    {
        $em = $this->getEntityManager();
        $query = $em->createQuery("SELECT t.label FROM WikiTagBundle:DocumentTag dt JOIN dt.tag t WHERE dt.document = :docid ORDER BY dt.tagOrder");
        $query = $query->setParameter("docid", $document);
        $result = $query->getScalarResult();
        $tagstr = array();
        foreach ($result as $res) {
            $tagstr[] = $res['label'];
        }
        return $tagstr;
    }
    
    
    /**
     * Update a wikitag document tags string.
     * @param DocumentInterface $document the wikitag document
     */
    function updateTagsStr(DocumentInterface $document)
    {
        
        $tagstr = $this->getTagsStr($document);
        
        $document->setTagsStr(implode(",",$tagstr));
        $this->getEntityManager()->persist($document);
    }
    

    /**
     * Search wikitag documents using the index.
     *
     * @param array $values : key: the fields to search into, value : array('value'=>value, 'weight'=>weight)
     * @param array $conditions : array : key : field name, value : simple value (operator is "=") or array(valuea, value2,...) (operatr is IN) or array("operator"=>"=","!=","<".">","<=".">=","like","ilike","in">, "value"=>value)
     * @param array $fields : array : a list of field name to include in the result
     * @return array [["id" => <the wikitag document id>, "externalId" => <the host document ids>, "score" => <the score for this document>]]
     */
    function search(array $values, array $conditions=null, array $fields=null)
    {
        $em = $this->getEntityManager();
        
        if(is_null($fields)) {
            $fieldnamelist = array();
        }
        else {
            $fieldnamelist = $fields;
        }

        $rsm = new ResultSetMapping();
        $rsm->addEntityResult("IRI\Bundle\WikiTagBundle\Entity\Document", "d");
        $rsm->addFieldResult("d", "id", "id");
        foreach($fieldnamelist as $fieldname) {
            $rsm->addFieldResult("d", $fieldname, $fieldname);
        }
        $rsm->addScalarResult("score", "score");
        $rsm->addMetaResult("d", "external_id", "external_id");
        
        
        $score = array();
        $i = 0;
        foreach ($values as $fielddef) {
            $i++;
            $field_list = explode(",", $fielddef["columns"]);
            $column_list = array();
            foreach($field_list as $field_name)
            {
                $column_list[] = $this->getColumnName(trim($field_name));
            }
            $columns = join(",", $column_list);
            
            $value = $fielddef["value"];
            $weight = isset($fielddef["weight"])?$fielddef["weight"]:1.0;
            
            $score[] = "(MATCH($columns) AGAINST (:value_$i))*:weight_$i";
            $parameters["value_$i"] = $value;
            $parameters["weight_$i"] = $weight;
        }
        
        $score_def = "(".implode("+", $score).")";
        
        $conditions_str = "";
        
        if(!is_null($conditions))
        {
            $conditions_array = array();
            $i = 0;
            foreach ($conditions as $field => $conddef)
            {
                if(!$field) {
                    continue;
                }
                $i++;
                $col = $this->getColumnName($field);
                if(is_array($conddef) && isset($conddef['operator']))
                {
                    $operator = $conddef["operator"];
                    $values = $conddef["value"];
                }
                elseif(is_array($conddef))
                {
                    $operator = "IN";
                    $values = $conddef;
                }
                else
                {
                    $operator = "=";
                    $values = $conddef;
                }
                
                if(!in_array( strtolower($operator), array("=","!=","<".">","<=".">=","like","ilike"))) {
                    throw new Exception('DocumentRepository.search : operator must be in "=","!=","<".">","<=".">=","like","ilike"');
                }
                   
                if($operator === "IN")
                {
                    $in_parameters = array();
                    for ($j = 0; $j < count($values); $j++) {
                        $parameters["cond_val_$i_$j"] = $values[$j];
                        $in_parameters[] = ":cond_val_$i_$j";
                    }
                    $cond = "($col IN (".implode(",",$in_parameters)."))";
                }
                else
                {
                    $cond = "($col $operator :cond_val_$i)";
                    $parameters["cond_val_$i"] = $values;
                }
                $conditions_array[] = $cond;
            }
            
            if(count($conditions_array) > 0)
            {
                $conditions_str = " AND ".implode(" AND ", $conditions_array);
            }
                
        }
        $fieldnamequery = "d.id, d.external_id";
        if(count($fieldnamelist) > 0) {
            $fieldnamequery .= ", d.".join(", d.", $fieldnamelist);
        }
        $query = $em->createNativeQuery("SELECT $fieldnamequery, $score_def AS score FROM wikitag_document d WHERE $score_def > 0  $conditions_str ORDER BY score DESC", $rsm);
        
        $query->setParameters($parameters);
        
        $res = $query->getResult();
        
        return $res;
    }
    
    /**
     * Copy the tahg lst from one document instance to another
     * @param IRI\Bundle\WikitagBundle\Model\DocumentInterface $src_doc
     * @param IRI\Bundle\WikitagBundle\Model\DocumentInterface $tgt_doc
     */
    public function copyTags($src_doc, $tgt_doc)
    {
        //remove the previous tags
        if(!is_null($tgt_doc->getTags())) {
            foreach ($tgt_doc->getTags() as $doctag) {
                $this->getEntityManager()->remove($doctag);
            }
        }
        
        // add the new ones
        if(!is_null($src_doc->getTags())) {
            foreach ($src_doc->getTags() as $doctag) {
                $new_doctag = clone $doctag;
                $new_doctag->setDocument($tgt_doc);
                $this->getEntityManager()->persist($new_doctag);
            }
        }
        
        $tgt_doc->setManualOrder(false);
        $this->getEntityManager()->persist($tgt_doc);
    }
        
}