Simple one hopefully, is there a specific way i should be updating a single database value using a model in Zend Framework.
I currently do this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
$_name = 'table';
public function updateSetting($id,$status)
{
$data = array(
'status' => $status
);
$this->update($data, 'id = '.$id);
}
}
$update = new Model_MyModel();
$update->updateSetting(10,1);
Obviously i could pass in another argument as the column to update. I just wondered if there was a more "magic" way i should be doing this?
You could write a simple property overloader for this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
protected $_name = 'table';
/**
* Should be a Zend_Db_Table_Row instance
*
* #var Zend_Db_Table_Row
*/
protected $_currentRow = null;
/**
* Property overloader
*
* For more information on this see
* http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members
*
* #param string $key
* #param string $value
* #return void
*/
public function __set($key, $value)
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->$key = $value;
}
else
{
throw new Exception('Cannot update a column on a non existent row!');
}
}
/**
* Save current row
*
* #return Model_MyModel
*/
public function saveCurrentRow()
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->save();
}
else
{
throw new Exception('Cannot save a non existent row!');
}
}
/**
* Set current row
*
* #param Zend_Db_Table_Row $row
* #return Model_MyModel
*/
public function setCurrentRow(Zend_Db_Table_Row $row)
{
$this->_currentRow = $row;
return $this;
}
/**
* Get current row
*
* #return Zend_Db_Table_Row
*/
public function getCurrentRow()
{
return $this->_currentRow;
}
}
You could then do stuff like this:
$model = new Model_MyModel();
$model->status = 'foo';
$model->somecolumn = 'bar'
$model->saveCurrentRow();
Although this approach would require the least editing in your code, an even better approach would be seperating your models from your database tables and use a Data Mapper Pattern as described in the Quickstart.
Related
I'm creating a content Element with gridelments where i need to render some Data from the childs directly. There is no problem for fields like "bodytext" or "header". But the assets gives me only a counter, not a reference or path.
So the big question: How can i render the assets images from the childs?
I can share a VH I just did
<?php
namespace GeorgRinger\Theme\ViewHelpers;
use TYPO3\CMS\Core\Database\DatabaseConnection;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Frontend\Resource\FileCollector;
class FalViewHelper extends AbstractViewHelper
{
/**
* #var boolean
*/
protected $escapeOutput = FALSE;
/**
* #param string $table
* #param string $field
* #param string $id
* #param string $as
* #return string
*/
public function render($table, $field, $id, $as = 'references')
{
$row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', $table, 'uid=' . (int)$id);
if (!$row) {
return '';
}
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
$fileCollector->addFilesFromRelation($table, $field, $row);
$this->templateVariableContainer->add($as, $fileCollector->getFiles());
$output = $this->renderChildren();
$this->templateVariableContainer->remove($as);
return $output;
}
/**
* #return DatabaseConnection
*/
protected function getDatabaseConnection()
{
return $GLOBALS['TYPO3_DB'];
}
}
Of course you need to adopt the namspace.
Usage would be
<theme:fal table="tt_content" field="assets" id=" => the id <= ">
<f:debug>{references}</f:debug>
</theme:fal>
I need to hyrdate multiple objests in one form. Here is what I use:
Product Form - I have a form where I call three fieldsets
Product Fieldset
Promotion Fieldset
Category Fieldset
I have Models for all the necessary tables, here is an example for the product model:
class Product implements ProductInterface
{
/**
* #var int
*/
protected $Id;
/**
* #var string
*/
protected $Title;
/**
* #var float
*/
protected $Price;
/**
* #var string
*/
protected $Description;
/**
* #var string
*/
protected $Url;
/**
* #var \DateTime
*/
protected $DateAdded;
/**
* #var string
*/
protected $Image;
/**
* #var int
*/
protected $Status;
/**
* #return int
*/
public function getId()
{
return $this->Id;
}
/**
* #param int $Id
*/
public function setId($Id)
{
$this->Id = $Id;
}
/**
* #return string
*/
public function getTitle()
{
return $this->Title;
}
/**
* #param string $Title
*/
public function setTitle($Title)
{
$this->Title = $Title;
}
/**
* #return float
*/
public function getPrice()
{
return $this->Price;
}
/**
* #param float $Price
*/
public function setPrice($Price)
{
$this->Price = $Price;
}
/**
* #return string
*/
public function getDescription()
{
return $this->Description;
}
/**
* #param string $Description
*/
public function setDescription($Description)
{
$this->Description = $Description;
}
/**
* #return string
*/
public function getUrl()
{
return $this->Url;
}
/**
* #param string $Url
*/
public function setUrl($Url)
{
$this->Url = $Url;
}
/**
* #return \DateTime
*/
public function getDateAdded()
{
return $this->DateAdded;
}
/**
* #param \DateTime $DateAdded
*/
public function setDateAdded($DateAdded)
{
$this->DateAdded = $DateAdded;
}
/**
* #return string
*/
public function getImage()
{
return $this->Image;
}
/**
* #param string $Image
*/
public function setImage($Image)
{
$this->Image = $Image;
}
/**
* #return int
*/
public function getStatus()
{
return $this->Status;
}
/**
* #param int $Status
*/
public function setStatus($Status)
{
$this->Status = $Status;
}
In my controllers I want to bind the data to my view so I can edit them.
try {
$aProduct = $this->productService->findProduct($iId);
} catch (\Exception $ex) {
// ...
}
$form = new ProductForm();
$form->bind($aProduct);
In the first place I need to select all the necessary information from the DB. I join three tables product, promotion and category tables. I must return the data to my controller as objects and bind them in my form to be able to edit on the view page.
Please give me some ideas how to accomplish this so I can continue with my development. I am stuck.
I will appreciate all the links which can help me or give me any ideas/examples from the real life.
public function findProduct($Id)
{
$iId = (int) $Id;
$sql = new Sql($this->dbAdapter);
$select = $sql->select('product');
$select->join('promotion', 'promotion.ProductId = product.Id', array('Discount', 'StartDate', 'EndDate', 'PromotionDescription' => 'Description', 'PromotionStatus', 'Type'), 'left');
$select->join('producttocategory', 'producttocategory.ProductId = product.Id', array('CategoryId'), 'left');
$select->join('category', 'category.Id = producttocategory.CategoryId', array('ParentId', 'Title', 'Description', 'Url', 'DateAdded', 'Image', 'Status'), 'left');
$where = new Where();
$where->equalTo('product.Id', $iId);
$select->where($where);
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
if ($result instanceof ResultInterface && $result->isQueryResult()) {
$resultSet = new HydratingResultSet($this->hydrator, $this->productPrototype);
return $resultSet->initialize($result);
}
throw new \Exception("Could not find row $Id");
}
I need to hydrate the result and return an object which I will use in the controller to bind the form.
You can to fill entities from a database manually.
If you want to fill automatically need to create a map between a database and entities. I made a library for making a map between DB and entities use annotations in entities https://github.com/newage/annotations.
Next step.
When you get different data from tables. Example:
SELECT
table1.id AS table1.id,
table1.title AS table1.title,
table2.id AS table2.id,
table2.alias AS table2.alias
FROM table1
JOIN table2 ON table1.id = table2.id
Need do foreach by rows and set data to entities comparing row with table name and Entity from a generated map.
Auto generating tree of entities from DB is my next project.
But it's do not finished. https://github.com/newage/zf2-simple-orm.
I am trying to develop a custom extension using TYPO3 extbase repository method. I want to fetch tt_content "pi_flexform" field value in my extension.
For achieving this I've followed persistence method.
In typoscript configuration files I've added like this;
plugin.tx_myext{
persistence {
classes {
\TYPO3\MyExt\Domain\Model\Myext {
mapping {
tableName = tt_content
recordType = \TYPO3\MyExt\Domain\Model\Myext
columns {
pi_flexform.mapOnProperty = piflexform
}
}
}
}
}
}
In my model file I've implemented the following code;
/**
* #var string
*/
protected $piflexform;
/**
* #return the $piflexform
*/
public function getPiflexform()
{
return $this->piflexform;
}
/**
* #param string $piflexform
*/
public function setPiflexform($piflexform)
{
$this->piflexform = $piflexform;
}
And also in the repository file;
$queryStmt = "SELECT pi_flexform FROM tt_content WHERE CType = 'dce_dce10' AND pid = 1 AND sys_language_uid = 0";
$query = $this->createQuery();
$query->statement( $queryStmt );
return $query->execute();
But it returns an empty object.
TYPO3\CMS\Extbase\Persistence\Generic\QueryResultprototypeobject (empty)
0 => TYPO3\VstRssfeed\Domain\Model\Vstrssfeedprototypepersistent entity (uid=0, pid=0)
piflexform => NULL
uid => 0 (integer)
_localizedUid => 0 (integer)modified
_languageUid => 0 (integer)modified
_versionedUid => 0 (integer)modified
pid => 0 (integer)
Can you guys please help me to solve this problem? Thanks in advance.
How to Effectively use the Repository and Query Object of Extbase?
http://blog.typoplanet.de/2010/01/27/the-repository-and-query-object-of-extbase/
Here's an example how your repository should look like:
myext/Classes/Domain/Repository/ContentRepository.php
<?php
namespace TYPO3\MyExt\Domain\Repository;
/**
* Class ContentRepository
* #package TYPO3\MyExt\Domain\Repository
*/
class ContentRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
/**
* Initializes the repository.
*
* #return void
*/
public function initializeObject() {
/** #var $querySettings \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings */
$querySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($querySettings);
}
/**
* Find By CType
*
* #param string $cType
* #param integer $pid
* #return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findByCtype($cType, $pid = FALSE) {
$constraints = array();
$query = $this->createQuery();
$constraints[] = $query->equals('CType', $cType);
if ($pid) {
$constraints[] = $query->equals('pid', $pid);
}
if ($constraints) {
if (count($constraints) == 1) {
$query->matching($constraints[0]);
} else {
$query->matching($query->logicalAnd($constraints));
}
}
return $query->execute();
}
}
And here's an example how your controller should do the work:
myext/Classes/Controller/SomeController.php
class SomeController extends \TYPO3\CMS\E... {
/**
* #var \TYPO3\MyExt\Domain\Repository\ContentRepository
* #inject
*/
protected $contentRepository = NULL;
public function someAction() {
$contents = $this->contentRepository->findByCtype('dce_dce10');
if ($contents) {
foreach ($contents as $content) {
echo '<h1>'.$content->getUid().'</h1>';
echo PHP_EOL.'<br />';
echo $content->getPiflexform();
echo PHP_EOL.'<br />';
}
}
}
}
I have this classes:
class Country
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $iso;
/**
* #MongoDB\EmbedOne(targetDocument="Localstring")
*/
protected $name;
public function __construct(){
$this->name = new Localstring();
}
}
class Localstring
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\Hash
*/
private $location = array();
}
I want to update every country with a new translation:
$dm = $this->get('doctrine_mongodb')
->getManager();
foreach ($json as $iso => $name) {
$country = $dm->getRepository('ExampleCountryBundle:Country')->findOneByIso($iso);
$localstring_name = $country->getName();
$localstring_name->addTranslation('es_ES', $name);
$dm->flush();
}
If I print one object just before flushing it prints correctly:
Example\CountryBundle\Document\Country Object ( [id:protected] => 541fe9c678f965b321241121 [iso:protected] => AF [name:protected] => Example\CountryBundle\Document\Localstring Object ( [id:Example\CountryBundle\Document\Localstring:private] => 541fe9c678f965b321241122 [location:Example\CountryBundle\Document\Localstring:private] => Array ( [en_EN] => Afghanistan [es_ES] => Afganistán ) ) )
But on database it doesn't updates. I tried updating $iso and it works. Why this happens?
You forgot to persist your object. flush() just pushes into DB your changes that were registered by persist() (called with your object in argument). It needs to be here because you doesn't change your document. You just added the translation. This functionality covered by Translatable extension and doesn't tell to the Doctrine that your object was modified. And when Doctrine will prepare the changelist for query it will not find changes and will not create the query.
Your code should look like this:
$dm = $this->get('doctrine_mongodb')
->getManager();
foreach ($json as $iso => $name) {
$country = $dm->getRepository('ExampleCountryBundle:Country')->findOneByIso($iso);
$localstring_name = $country->getName();
$localstring_name->addTranslation('es_ES', $name);
$dm->persist($country);
}
$dm->flush();
you forgot the persist your object!
try this at the end of your foreach: $dm->persist($your_object);
and outside form foreach put the $dm->flush();
I have very specific problem with my Zend Framework application. I use my own Model class called My_Model which is singleton and I use it for calling DB models. Every model extends My_Db_Table class and has a row class that extends My_Db_Table_Row. When I call the model it is OK but the model seems to ignore row class model. Could anybody advise me what am I doing wrong?
The custom class:
class My_Model {
protected static $_instance = null;
protected $_services = array();
private function __construct() {
}
/**
* Service classed is called by it's name
*
* #param string $name
* #return My_Db_Table
*/
public function getService($name) {
if (!isset($this->_services[$name])) {
$service = new $name();
if (!$service instanceof Zend_Db_Table_Abstract) {
$type = gettype($service);
if ($type == 'object') {
$type = get_class($service);
}
throw new Zend_Db_Table_Row_Exception("Class must be a Zend_Db_Table_Abstract, but it is $type");
}
$this->_services[$name] = $service;
}
return $this->_services[$name];
}
}
The Zend_Db_Table classes are:
<?php
/**
* This class represents a DB table
*
*/
class My_Db_Table extends Zend_Db_Table_Abstract {
/**
* Fetches the table row by primary key
*
* #param string $id
* #return Product
*/
public function getById($id) {
return $this->find($id)->current();
}
}
Row class:
<?php
/**
* Row class
*
*/
class My_Db_Table_Row extends Zend_Db_Table_Row_Abstract {
/**
* Inflector to get the attribute name
* camelCase -> under_score
*
* #param string $columnName
* #return string
*/
protected function _transformColumn($columnName) {
$inflector = new Zend_Filter_Inflector ( ":string" );
$inflector->setRules ( array (
':string' => array ('Word_CamelCaseToUnderscore', 'StringToLower' ) ) )
;
$columnName = $inflector->filter ( array ('string' => $columnName ) );
return $columnName;
}
/**
* Magic method hook to catch getters and setters
*
* #param string $method
* #param array $args
*/
public function __call($method, array $args) {
$matches = array ();
if (preg_match ( '/^get(\w+?)$/', $method, $matches )) {
$attribute = $matches [1];
return $this->{$attribute};
}
if (preg_match ( '/^set(\w+?)$/', $method, $matches )) {
$attribute = $matches [1];
$this->{$attribute} = (count ( $args ) == 1) ? $args [0] : null;
return;
}
return parent::__call ( $method, $args );
}
}
To use these classe I use following code:
$record = My_Model::getInstance ()->getService ( 'Users' )->getById ( 1 );
$record->updateFromArray(array('auth_token' => 'asfsgfgswg'));
And I get the following error message:
Message: Unrecognized method 'updateFromArray()'
Stack trace:
#0 /home/www/fbdrives/application/modules/facebook/controllers/IndexController.php(34): Zend_Db_Table_Row_Abstract->__call('updateFromArray', Array)
#1 /home/www/fbdrives/application/modules/facebook/controllers/IndexController.php(34): Zend_Db_Table_Row->updateFromArray(Array)
#2 /home/www/fbdrives/library/Zend/Controller/Action.php(513): Facebook_IndexController->indexAction()
#3 /home/www/fbdrives/library/Zend/Controller/Dispatcher/Standard.php(295): Zend_Controller_Action->dispatch('indexAction')
#4 /home/www/fbdrives/library/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#5 /home/www/fbdrives/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend_Controller_Front->dispatch()
#6 /home/www/fbdrives/library/Zend/Application.php(366): Zend_Application_Bootstrap_Bootstrap->run()
#7 /home/www/fbdrives/public/index.php(27): Zend_Application->run()
#8 {main}
The Users class:
<?php
/**
* User table model class
*
*/
class Users extends My_Db_Table {
/**
* The DB table name
*
* #var string
*/
protected $_name = 'user';
/**
* The row class name
*
* #var string
*/
protected $_rowclass = 'User';
}
The User class that is not called with updateFromArray method:
<?php
/**
* The user model class
*
*/
class User extends My_Db_Table_Row {
/**
* Update values by array values
*
* #param array $values
*/
public function updateFromArray(array $values) {
$this->setFromArray($values);
$this->save();
return $this;
}
}
I will provide any necessary info if needed. I am really stucked on this one for a few days now and nothing seems to help. The method updateFromArray is in the User class but the class is not loaded properly.
Thanks in advance for every help!
I think the problem may be that you are using $_rowclass and not $_rowClass (with an uppercase C).
If you put this line instead, it may work:
protected $_rowClass = 'User';
That's why in the stack trace you can see Zend_Db_Table being called, which is the default class for a a row that hasn't been set to use a custom class.