Exemple #1
0
 public function performIndex()
 {
     $this->prepareControl();
     if ($this->isListing) {
         /*
          * Provide default action on set of selected model's items.
          */
         $this->browser->setPagerVolatility("none");
         if (is_callable($this->browserCustomizer)) {
             call_user_func($this->browserCustomizer, $this, $this->browser);
         } else {
             // try generate some commonly useful databrowser
             $controller = $this;
             $this->browser->addColumn('label', \de\toxa\txf\_L('Label'), false, function ($value, $propName, $record, $id) use($controller) {
                 return $controller->getModel()->getMethod('select')->invoke(null, $controller->source, $id)->describe();
             })->setRowCommander(function ($id, $data) use($controller) {
                 $items = array('view' => markup::link(sprintf($controller->getUrls()->view, $id), \de\toxa\txf\_L('view item'), 'controller-list-action-view'), 'edit' => markup::link(sprintf($controller->getUrls()->edit, $id), \de\toxa\txf\_L('edit item'), 'controller-list-action-edit'), 'delete' => markup::link(sprintf($controller->getUrls()->delete, $id), \de\toxa\txf\_L('delete item'), 'controller-list-action-delete'));
                 if (!user::current()->isAuthenticated()) {
                     unset($items['edit'], $items['delete']);
                 }
                 return implode(' ', array_filter($items));
             });
             if (user::current()->isAuthenticated()) {
                 $controller->setPanelControl('add', markup::link($controller->getUrls()->add, \de\toxa\txf\_L('add item'), 'controller-list-action-add'));
             }
         }
         if (user::current()->isAuthenticated()) {
             if (!$this->hasPanelControl('add')) {
                 $this->setPanelControl('add', markup::link(context::selfURL(false, array_merge(array_pad(array(), $this->modelClass->getMethod('idSize')->invoke(null), 0), array('edit'))), \de\toxa\txf\_L('Add')));
             }
         }
         $this->browser->processInput();
         return $this->browser->getCode();
     }
     /**
      * Provide default action on single instance of selected model.
      */
     $this->editor->mayEdit(false)->mayDelete(false);
     if (is_callable($this->viewCustomizer)) {
         call_user_func($this->viewCustomizer, $this, false);
     } else {
         $data = $this->editor->item()->published();
         // reduce relations
         foreach ($data as $key => $value) {
             if (is_array($value)) {
                 $data[$key] = implode(', ', $value);
             }
         }
         return html::arrayToCard($data);
     }
     if (user::current()->isAuthenticated()) {
         if (!$this->hasPanelControl('edit')) {
             $this->setPanelControl('edit', markup::link(context::selfURL(false, array_merge($this->item->id(), array('edit'))), \de\toxa\txf\_L('Edit')));
         }
     }
     if ($this->editor->processInput($this->editorValidator)) {
         txf::redirectTo($this->getUrls()->list);
     }
     return $this->editor->render();
 }
Exemple #2
0
 /**
  * Associates editor instance with single item of related model.
  *
  * Providing null is available for convenience, too. In that case editor is
  * actually kept unassociated with a particular item of model, but returns
  * true here nevertheless. It's intended to call selectItem( null ) in case
  * of trying to edit model instance that does not exist, yet. This way
  * callers won't have to test for existing item themselves.
  *
  * It is possible to provide item instance instead of item's ID here. In
  * that case editor is switching to use provided item's data source further
  * one.
  *
  * Selecting item is available once, only. This method is throwing exception
  * on trying to select another item.
  *
  * @param mixed $id ID or instance of item to associate with editor
  * @return bool true on success, false on error
  * @throws \LogicException on trying to re-associate editor
  */
 public function selectItem($id)
 {
     if ($this->item) {
         throw new \LogicException(\de\toxa\txf\_L('Editor is already operating on model instance.'));
     }
     if ($id !== null) {
         if (is_object($id) && $this->class->isInstance($id)) {
             $this->item = $id;
         } else {
             $this->item = $this->class->getMethod('select')->invoke(null, $this->datasource, $id);
         }
         if (!$this->item) {
             return false;
         }
         $this->datasource = $this->item->source();
         if (!$this->datasource) {
             throw new \RuntimeException('item is not associated with data source');
         }
         foreach ($this->fields as $field) {
             /** @var model_editor_field $field */
             $field->type()->onSelectingItem($this, $this->item, $field);
         }
     }
     return true;
 }
Exemple #3
0
 /**
 * Retrieves relation according to declaration selected by given name.
 *
 * Relations may be declared in static property $relations of a model.
 *
 * @example
 
    - have user relate to address via immediately related person
 		  (user.person_id => person.id - person.address_id => contact.id)
 
 		user::$relations = array(
        'contact_address' => array(             // <- declaring name of relation
 				'referencing' => array(             // <- declaring user referring to some
 					'model'  => 'person',           // <- ... person. ...
 					'with'   => 'person_id',        // <- declaring user.person_id to contain foreign key of ...
 					'on'     => 'id',               // <- ... person.id    thus:  user.person_id => person.id
 					'referencing' => array(         // <- declaring person is then referring to another model ...
 						'model' => 'address',       // <- ... address ...
 						'alias' => 'contact',       // <- ... calling it "contact" here ...
 						'with'  => 'address_id',    // <- ... with person.address_id containing foreign key of ...
 						'on'    => 'id',            // <- ... contact.id   thus:  person.address_id => contact.id
 					)
 				)
 			),
 		);
 
 		- same relation declared in reverse direction
 		  (contact.id <= person.address_id - person.id <= user.person_id)
 
 		address::$relations = array(
 			'user' => array(                        // <- declaring name of relation
            'alias' => 'contact',               // <- declaring alias of initial node in relation being contact
 				'referencedBy' => array(            // <- declaring address (called contact) referred to by some
 					'model'  => 'person',           // <- ... person. ...
 					'with'   => 'address_id',       // <- ... using its property address_id to contain foreign key of ...
 					'on'     => 'id',               // <- ... contact.id    thus:  contact.id <= person.address_id
 					'referencedBy' => array(        // <- declaring person is then referred to by another model ...
 						'model' => 'user',          // <- ... user ...
 						'with'  => 'person_id',     // <- ... using its property person_id to contain foreign key of ...
 						'on'    => 'id',            // <- ... person.id     thus:  person.id <= user.person_id
 					)
 				)
 			),
 		);
 
    - example of an m:n-relation
           (customer.id <= ordered.customer_id - ordered.product_id => goods.id)
 
 		customer::$relations = array(
        'ordered_goods' => array(
 				'referencedBy' => array(
 					'model' => 'ordered',
 					'with'  => 'customer_id',
 					'on'    => 'id',
 					'referencing' => array(
 						'model' => 'product',
 						'alias' => 'goods',
 						'with'  => 'product_id',
 						'on'    => 'id',
 					)
 				)
 			),
 		);
 
    - same m:n-relation with multi-dimensional reference
           (customer.id <= ordered.customer_id - [ordered.type,ordered.product_id] => [goods.type,goods.id])
 
 		customer::$relations = array(
        'ordered_goods' => array(
 				'referencedBy' => array(
 					'model' => 'ordered',
 					'with'  => 'customer_id',
 					'on'    => 'id',
 					'referencing' => array(
 						'model' => 'product',
 						'alias' => 'goods',
 						'with'  => array( 'type', 'product_id' ),
 						'on'    => array( 'type', 'id' ),
 					)
 				)
 			),
 		);
 
    - again, same m:n-relation with multi-dimensional reference, this time
      relying on implicitly inserted virtual model (named explicitly)
           (customer.id <= ordered.customer_id - [ordered.type,ordered.product_id] => [goods.type,goods.id])
 
 		customer::$relations = array(
        'ordered_goods' => array(
 				'referencedBy' => array(
 					'dataset' => 'ordered',
 					'with'    => 'customer_id',
 					'on'      => 'id',
 					'referencing' => array(
 						'model' => 'product',
 						'alias' => 'goods',
 						'with'  => array( 'type', 'product_id' ),
 						'on'    => array( 'type', 'id' ),
 					)
 				)
 			),
 		);
 
    - again, same m:n-relation with multi-dimensional reference, this time
      relying on implicitly inserted virtual model (not named explicitly)
           (customer.id <= customer_goods.customer_id - [customer_product.type,customer_product.product_id] => [goods.type,goods.id])
 
      The inner set's name is derived by combining names of neighbouring sets.
 
 		customer::$relations = array(
        'ordered_goods' => array(
 				'referencedBy' => array(
 					'with'    => 'customer_id',
 					'on'      => 'id',
 					'referencing' => array(
 						'model' => 'product',
 						'alias' => 'goods',
 						'with'  => array( 'type', 'product_id' ),
 						'on'    => array( 'type', 'id' ),
 					)
 				)
 			),
 		);
 
    - finally, same m:n-relation with multi-dimensional reference, this time
      relying on implicitly inserted virtual model (not named explicitly) and
      deriving property names from referenced properties' names
           (customer.id <= customer_product.customer_id - [customer_product.product_type,customer_product.product_id] => [goods.type,goods.id])
 
      The inner set's name is derived by combining names of neighbouring sets.
 
 		customer::$relations = array(
        'ordered_goods' => array(
 				'referencedBy' => array(
 					'on' => 'id',
 					'referencing' => array(
 						'model' => 'product',
 						'alias' => 'goods',
 						'on'    => array( 'type', 'id' ),
 					)
 				)
 			),
 		);
 
 *
 * @param string $name name of model's relation to retrieve
 * @param model $bindOnInstance instance of current model to bind relation on
 * @return model_relation
 */
 public static function relation($name, model $bindOnInstance = null)
 {
     $name = data::isKeyword($name);
     if (!$name) {
         throw new \InvalidArgumentException('invalid relation name');
     }
     $fullName = static::getReflection()->getName() . '::' . $name;
     if (array_key_exists($fullName, self::$_relationCache)) {
         // read existing relation from runtime cache
         $relation = self::$_relationCache[$fullName];
     } else {
         if (!array_key_exists($name, static::$relations)) {
             throw new \InvalidArgumentException(sprintf('no such relation: %s', $name));
         }
         try {
             $relation = static::_compileRelation(null, static::$relations[$name]);
             if (!$relation->isComplete()) {
                 throw new \InvalidArgumentException('incomplete relation definition: %s');
             }
             $relation->setName($name);
             // write this relation (unbound) into runtime cache
             self::$_relationCache[$fullName] = $relation;
         } catch (\InvalidArgumentException $e) {
             throw new \InvalidArgumentException(sprintf('%s: %s', $e->getMessage(), $name), $e->getCode(), $e);
         }
     }
     // clone cached relation
     $relation = clone $relation;
     // bind on provided instance if that is a subclass of current one
     if ($bindOnInstance) {
         $expectedClass = new \ReflectionClass(get_called_class());
         $givenClass = $bindOnInstance->getReflection();
         if ($givenClass->getName() !== $expectedClass->getName() && !$givenClass->isSubclassOf($expectedClass)) {
             throw new \InvalidArgumentException('provided instance is not compatible');
         }
         $relation->bindNodeOnItem(0, $bindOnInstance);
     }
     return $relation;
 }
Exemple #4
0
 /**
  * Detects if provided model is compatible with current one.
  *
  * A model is considered _compatible_ if it is same one as current one or
  * derived from current one.
  *
  * @param model|\ReflectionClass|string|model_relation_model $testedModel
  * @return bool true if provided model is compatible with current one
  */
 public function isCompatibleModel($testedModel)
 {
     if ($this->isVirtual()) {
         // there is no support for derivation on virtual models
         return $this->isSameModel($testedModel);
     }
     $testedModel = model::normalizeModel($testedModel);
     return $testedModel->getName() == $this->reflection->getName() || $testedModel->isSubclassOf($this->reflection);
 }