Source for file Parser.php

Documentation is available at Parser.php

  1. <?php
  2. /*
  3.  *  $Id: Table.php 1397 2007-05-19 19:54:15Z zYne $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information, see
  19.  * <http://www.phpdoctrine.com>.
  20.  */
  21. /**
  22.  * Doctrine_Relation_Parser
  23.  *
  24.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  25.  * @package     Doctrine
  26.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  27.  * @version     $Revision: 1397 $
  28.  * @category    Object Relational Mapping
  29.  * @link        www.phpdoctrine.com
  30.  * @since       1.0
  31.  */
  32. {
  33.     /**
  34.      * @var Doctrine_Table $_table          the table object this parser belongs to
  35.      */
  36.     protected $_table;
  37.     /**
  38.      * @var array $_relations               an array containing all the Doctrine_Relation objects for this table
  39.      */
  40.     protected $_relations = array();
  41.     /**
  42.      * @var array $_pending                 relations waiting for parsing
  43.      */
  44.     protected $_pending   = array();
  45.     /**
  46.      * constructor
  47.      *
  48.      * @param Doctrine_Table $table         the table object this parser belongs to
  49.      */
  50.     public function __construct(Doctrine_Table $table
  51.     {
  52.         $this->_table = $table;
  53.     }
  54.     /**
  55.      * getTable
  56.      *
  57.      * @return Doctrine_Table   the table object this parser belongs to
  58.      */
  59.     public function getTable()
  60.     {
  61.         return $this->_table;
  62.     }
  63.     /**
  64.      * getPendingRelation
  65.      *
  66.      * @return array            an array defining a pending relation
  67.      */
  68.     public function getPendingRelation($name
  69.     {
  70.         if isset($this->_pending[$name])) {
  71.             throw new Doctrine_Relation_Exception('Unknown pending relation ' $name);
  72.         }
  73.         
  74.         return $this->_pending[$name];
  75.     }
  76.     
  77.     public function hasRelation($name)
  78.     {
  79.         if isset($this->_pending[$name]&& isset($this->_relations[$name])) {
  80.             return false;
  81.         }
  82.         
  83.         return true;
  84.     }
  85.     /**
  86.      * binds a relation
  87.      *
  88.      * @param string $name 
  89.      * @param string $field 
  90.      * @return void 
  91.      */
  92.     public function bind($name$options array())
  93.     {
  94.         if (isset($this->relations[$name])) {
  95.             unset($this->relations[$name]);
  96.         }
  97.  
  98.         $lower strtolower($name);
  99.  
  100.         if ($this->_table->hasColumn($lower)) {
  101.             throw new Doctrine_Relation_Exception("Couldn't bind relation. Column with name " $lower ' already exists!');
  102.         }
  103.  
  104.         $e    explode(' as '$name);
  105.         $name $e[0];
  106.         $alias = isset($e[1]$e[1$name;
  107.  
  108.         if isset($options['type'])) {
  109.             throw new Doctrine_Relation_Exception('Relation type not set.');
  110.         }
  111.  
  112.         $this->_pending[$aliasarray_merge($optionsarray('class' => $name'alias' => $alias));
  113.  
  114.         $m Doctrine_Manager::getInstance();
  115.         
  116.         if (isset($options['onDelete'])) {
  117.             $m->addDeleteAction($name$this->_table->getComponentName()$options['onDelete']);
  118.         }
  119.         if (isset($options['onUpdate'])) {
  120.             $m->addUpdateAction($name$this->_table->getComponentName()$options['onUpdate']);
  121.         }
  122.  
  123.         return $this->_pending[$alias];
  124.     }
  125.     /**
  126.      * getRelation
  127.      *
  128.      * @param string $alias      relation alias
  129.      */
  130.     public function getRelation($alias$recursive true)
  131.     {
  132.         if (isset($this->_relations[$alias])) {
  133.             return $this->_relations[$alias];
  134.         }
  135.  
  136.         if (isset($this->_pending[$alias])) {
  137.             $def $this->_pending[$alias];
  138.         
  139.             // check if reference class name exists
  140.             // if it does we are dealing with association relation
  141.             if (isset($def['refClass'])) {
  142.                 $def $this->completeAssocDefinition($def);
  143.                 $localClasses array_merge($this->_table->getOption('parents')array($this->_table->getComponentName()));
  144.  
  145.                 if isset($this->_pending[$def['refClass']]&& 
  146.                      isset($this->_relations[$def['refClass']])) {
  147.  
  148.                     $parser $def['refTable']->getRelationParser();
  149.                     if $parser->hasRelation($this->_table->getComponentName())) {
  150.                         $parser->bind($this->_table->getComponentName(),
  151.                                       array('type'    => Doctrine_Relation::ONE,
  152.                                             'local'   => $def['local'],
  153.                                             'foreign' => $this->_table->getIdentifier(),
  154.                                             'localKey' => true,
  155.                                             ));
  156.                     }
  157.  
  158.                     if $this->hasRelation($def['refClass'])) {
  159.                         $this->bind($def['refClass']array('type' => Doctrine_Relation::MANY,
  160.                                                             'foreign' => $def['local'],
  161.                                                             'local'   => $this->_table->getIdentifier()));
  162.                     }
  163.                 }
  164.                 if (in_array($def['class']$localClasses)) {
  165.                     $rel new Doctrine_Relation_Nest($def);
  166.                 else {
  167.                     $rel new Doctrine_Relation_Association($def);
  168.                 }
  169.             else {
  170.                 // simple foreign key relation
  171.                 $def $this->completeDefinition($def);
  172.  
  173.                 if (isset($def['localKey'])) {
  174.  
  175.                     $rel new Doctrine_Relation_LocalKey($def);
  176.                 else {
  177.                     $rel new Doctrine_Relation_ForeignKey($def);
  178.                 }
  179.             }
  180.             if (isset($rel)) {
  181.                 // unset pending relation
  182.                 unset($this->_pending[$alias]);
  183.  
  184.                 $this->_relations[$alias$rel;
  185.                 return $rel;
  186.             }
  187.         }
  188.         if ($recursive{
  189.             $this->getRelations();
  190.  
  191.             return $this->getRelation($aliasfalse);
  192.         else {
  193.             throw new Doctrine_Table_Exception('Unknown relation alias ' $alias);
  194.         }
  195.     }
  196.     /**
  197.      * getRelations
  198.      * returns an array containing all relation objects
  199.      *
  200.      * @return array        an array of Doctrine_Relation objects
  201.      */
  202.     public function getRelations()
  203.     {
  204.         foreach ($this->_pending as $k => $v{
  205.             $this->getRelation($k);
  206.         }
  207.  
  208.         return $this->_relations;
  209.     }
  210.     /**
  211.      * getImpl
  212.      * returns the table class of the concrete implementation for given template
  213.      * if the given template is not a template then this method just returns the
  214.      * table class for the given record
  215.      *
  216.      * @param string $template 
  217.      */
  218.     public function getImpl($template)
  219.     {
  220.         $conn $this->_table->getConnection();
  221.  
  222.         if (in_array('Doctrine_Template'class_parents($template))) {
  223.             $impl $this->_table->getImpl($template);
  224.             
  225.             if ($impl === null{
  226.                 throw new Doctrine_Relation_Parser_Exception("Couldn't find concrete implementation for template " $template);
  227.             }
  228.         else {
  229.             $impl $template;
  230.         }
  231.  
  232.         return $conn->getTable($impl);
  233.     }
  234.     /**
  235.      * Completes the given association definition
  236.      *
  237.      * @param array $def    definition array to be completed
  238.      * @return array        completed definition array
  239.      */
  240.     public function completeAssocDefinition($def
  241.     {
  242.         $conn $this->_table->getConnection();
  243.         $def['table'$this->getImpl($def['class']);
  244.         $def['class'$def['table']->getComponentName();
  245.         $def['refTable'$this->getImpl($def['refClass']);
  246.  
  247.         $id $def['refTable']->getIdentifier();
  248.  
  249.         if (count($id1{
  250.             if isset($def['foreign'])) {
  251.                 // foreign key not set
  252.                 // try to guess the foreign key
  253.     
  254.                 $def['foreign'($def['local'=== $id[0]$id[1$id[0];
  255.             }
  256.             if isset($def['local'])) {
  257.                 // foreign key not set
  258.                 // try to guess the foreign key
  259.  
  260.                 $def['local'($def['foreign'=== $id[0]$id[1$id[0];
  261.             }
  262.         else {
  263.  
  264.             if isset($def['foreign'])) {
  265.                 // foreign key not set
  266.                 // try to guess the foreign key
  267.     
  268.                 $columns $this->getIdentifiers($def['table']);
  269.     
  270.                 $def['foreign'$columns;
  271.             }
  272.             if isset($def['local'])) {
  273.                 // local key not set
  274.                 // try to guess the local key
  275.                 $columns $this->getIdentifiers($this->_table);
  276.     
  277.                 $def['local'$columns;
  278.             }
  279.         }
  280.         return $def;
  281.     }
  282.     /** 
  283.      * getIdentifiers
  284.      * gives a list of identifiers from given table
  285.      *
  286.      * the identifiers are in format:
  287.      * [componentName].[identifier]
  288.      *
  289.      * @param Doctrine_Table $table     table object to retrieve identifiers from
  290.      */
  291.     public function getIdentifiers(Doctrine_Table $table)
  292.     {
  293.         if (is_array($table->getIdentifier())) {
  294.             $columns array();
  295.             foreach((array) $table->getIdentifier(as $identifier{
  296.                 $columns[strtolower($table->getComponentName())
  297.                            . '_' $table->getIdentifier();
  298.             }
  299.         else {
  300.             $columns strtolower($table->getComponentName())
  301.                            . '_' $table->getIdentifier();
  302.         }
  303.  
  304.         return $columns;
  305.     }
  306.     /**
  307.      * guessColumns
  308.      *
  309.      * @param array $classes                    an array of class names
  310.      * @param Doctrine_Table $foreignTable      foreign table object
  311.      * @return array                            an array of column names
  312.      */
  313.     public function guessColumns(array $classesDoctrine_Table $foreignTable)
  314.     {
  315.         $conn $this->_table->getConnection();
  316.  
  317.         foreach ($classes as $class{
  318.             try {
  319.                 $table   $conn->getTable($class);
  320.             catch (Doctrine_Table_Exception $e{
  321.                 continue;
  322.             }
  323.             $columns $this->getIdentifiers($table);
  324.             $found   true;
  325.  
  326.             foreach ((array) $columns as $column{
  327.                 if $foreignTable->hasColumn($column)) {
  328.                     $found false;
  329.                     break;
  330.                 }
  331.             }
  332.             if ($found{
  333.                 break;
  334.             }
  335.         }
  336.         
  337.         if $found{
  338.             throw new Doctrine_Relation_Exception("Couldn't find columns.");
  339.         }
  340.  
  341.         return $columns;
  342.     }
  343.     /**
  344.      * Completes the given definition
  345.      *
  346.      * @param array $def    definition array to be completed
  347.      * @return array        completed definition array
  348.      */
  349.     public function completeDefinition($def)
  350.     {
  351.         $conn $this->_table->getConnection();
  352.         $def['table'$this->getImpl($def['class']);
  353.         $def['class'$def['table']->getComponentName();
  354.  
  355.         $foreignClasses array_merge($def['table']->getOption('parents')array($def['class']));
  356.         $localClasses   array_merge($this->_table->getOption('parents')array($this->_table->getComponentName()));
  357.  
  358.         if (isset($def['local'])) {
  359.             if isset($def['foreign'])) {
  360.                 // local key is set, but foreign key is not
  361.                 // try to guess the foreign key
  362.  
  363.                 if ($def['local'=== $this->_table->getIdentifier()) {
  364.                     $def['foreign'$this->guessColumns($localClasses$def['table']);
  365.                 else {
  366.                     // the foreign field is likely to be the
  367.                     // identifier of the foreign class
  368.                     $def['foreign'$def['table']->getIdentifier();
  369.                     $def['localKey'true;
  370.                 }
  371.             else {
  372.                 if ($def['local'!== $this->_table->getIdentifier(&& 
  373.                     $def['type'== Doctrine_Relation::ONE{
  374.                     $def['localKey'true;
  375.                 }
  376.             }
  377.         else {
  378.             if (isset($def['foreign'])) {
  379.                 // local key not set, but foreign key is set
  380.                 // try to guess the local key
  381.                 if ($def['foreign'=== $def['table']->getIdentifier()) {
  382.                     $def['localKey'true;
  383.                     try {
  384.                         $def['local'$this->guessColumns($foreignClasses$this->_table);
  385.                     catch (Doctrine_Relation_Exception $e{
  386.                         $def['local'$this->_table->getIdentifier();
  387.                     }
  388.                 else {
  389.                     $def['local'$this->_table->getIdentifier();
  390.                 }
  391.             else {
  392.                 // neither local or foreign key is being set
  393.                 // try to guess both keys
  394.  
  395.                 $conn $this->_table->getConnection();
  396.  
  397.                 // the following loops are needed for covering inheritance
  398.                 foreach ($localClasses as $class{
  399.                     $table  $conn->getTable($class);
  400.                     $column strtolower($table->getComponentName())
  401.                             . '_' $table->getIdentifier();
  402.  
  403.                     foreach ($foreignClasses as $class2{
  404.                         $table2 $conn->getTable($class2);
  405.                         if ($table2->hasColumn($column)) {
  406.                             $def['foreign'$column;
  407.                             $def['local']   $table->getIdentifier();
  408.  
  409.                             return $def;
  410.                         }
  411.                     }
  412.                 }
  413.  
  414.                 foreach ($foreignClasses as $class{
  415.                     $table  $conn->getTable($class);
  416.                     $column strtolower($table->getComponentName())
  417.                             . '_' $table->getIdentifier();
  418.                 
  419.                     foreach ($localClasses as $class2{
  420.                         $table2 $conn->getTable($class2);
  421.                         if ($table2->hasColumn($column)) {
  422.                             $def['foreign']  $table->getIdentifier();
  423.                             $def['local']    $column;
  424.                             $def['localKey'true;
  425.                             return $def;
  426.                         }
  427.                     }
  428.                 }
  429.  
  430.                 // auto-add columns and auto-build relation
  431.                 $columns array();
  432.                 foreach ((array) $this->_table->getIdentifier(as $id{
  433.                     $column strtolower($table->getComponentName())
  434.                             . '_' $id;
  435.  
  436.                     $col $this->_table->getDefinitionOf($id);
  437.                     $type $col['type'];
  438.                     $length $col['length'];
  439.  
  440.                     unset($col['type']);
  441.                     unset($col['length']);
  442.                     unset($col['autoincrement']);
  443.                     unset($col['sequence']);
  444.                     unset($col['primary']);
  445.  
  446.                     $def['table']->setColumn($column$type$length$col);
  447.                     
  448.                     $columns[$column;
  449.                 }
  450.                 if (count($columns1{
  451.                     $def['foreign'$columns;
  452.                 else {
  453.                     $def['foreign'$columns[0];
  454.                 }
  455.                 $def['local'$this->_table->getIdentifier();
  456.             }
  457.         }
  458.         return $def;
  459.     }
  460. }