Criando um Model Passo-a-Passo
Supondo que já tenhamos nosso PDO e Adapter corretamente criados no documento de configuração, vamos começar a criar o nosso model customizado. Primeiro passo é sabermos que podemos criar nossa classe model que tenha um objeto TableGateway interno, que usaremos para realizar as operações no banco de dados, ou podemos fazer com que nossa classe herde do AbstractTableGateway e de certa forma nosso model ser um TableGateway customizado.
Model herdando de AbstractTableGateway
Vamos optar inicialmente pela herança. Então nossa classe ficaria assim:
namespace Application\Model;
use \Zend\Db\TableGateway\AbstractTableGateway;
class ProductModel extends AbstractTableGateway {
}
|
Lembrando que a classe AbstractTableGateway precisa que haja o adapter e o nome da tabela para que seu método initialize(), executa sempre antes de qualquer operação, retorne verdadeiro e não impeça sua execução. Então, vamos obrigar o adapter no construtor e sobrescrever o atributo $table com o nome da nossa tabela.
namespace Application\Model;
use \Zend\Db\TableGateway\AbstractTableGateway;
use \Zend\Db\Adapter\Adapter;
class ProductModel extends AbstractTableGateway {
protected $table = ‘product’;
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
$this->initialize();
}
}
|
O problema com o objeto Sql
Você pode está se perguntando porque chamar o método initialize() dentro do construtor se ele será chamado sempre antes de qualquer operação. Esse cria o objeto Sql internamente à partir do nosso adapter, e isso é essêncial caso precisemos usá-lo logo após a instanciação e antes da executação de algum de seus métodos.
Veja a criação do objeto Sql dentro do método initialize() à partir do adapter e nome da tabela.
public function initialize()
{
if ($this->isInitialized) {
return;
}
/…
if (!$this->sql instanceof Sql) {
$this->sql = new Sql($this->adapter, $this->table);
}
/…
$this->isInitialized = true;
}
|
Agora imaginemos um caso onde precisamos usar o obejto Sql logo após a instanciação do nosso Model e antes da executação de algum de seus métodos.
class ExampleController extends AbstractActionController
{
$adapter = $this->getServiceLocator()->get(‘Zend\Db\Adapter\Adapter’);
$productModel = new ProductModel($adapter);
$select = $productModel->getSql()->select();
$select->where(array(‘id’ => ‘12’));
$select->order(‘name desc’);
$productModel->selectWith($select);
return array();
}
|
Se formos olhar o código do método selectWith() do AbstractTableGateway veremos que a primeira ação a ser realizada é chamar o método initialize(). Mas antes disse esse método não é naturalmente chamado e então como pegariamos o objeto Sql para capturarmos um objeto Select feito no código $productModel->getSql()->select()? Nosso objeto Sql não foi criado internamente ainda, não naturalmente. Por isso executamor os initialize() no início de nosso construtor.
Entidade
Para seguir criando nosso métodos, vamos antes imaginar uma entidade Product.
namespace Application\Entity;
class Product {
private $id;
private $name;
private $amount;
/.. getters and setters
}
|
Métodos de comunicação com o Banco de Dados
Sendo assim, vamos agora criar nossos métodos de comunicação com o banco de dados, como por exemplo, insert, update, delete e alguns tipos de select.
namespace Application\Model;
use \Zend\Db\TableGateway\AbstractTableGateway;
use \Zend\Db\Adapter\Adapter;
class ProductModel extends AbstractTableGateway {
protected $table = ‘product’;
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
$this->initialize();
}
public function insert(Entity\Product $product)
{
$data = array(
‘name’=> $product->getName(),
‘amount’ => $material->getAmount(),
);
$this->insert($data);
}
public function update(Entity\Product $product)
{
$data = array(
‘name’=> $product->getName(),
‘amount’ => $material->getAmount(),
);
$this->update($data, array(‘id’ => $product->getId()));
}
public function delete($id)
{
$this->delete(array(‘id’ => $id));
}
public function findAll()
{
/* Retornar Array contendo objetos da entidade Objeto*/
$this->resultSetPrototype = new ResultSet();
$this->resultSetPrototype->setArrayObjectPrototype(new Entity\Product());
return $this->select();
}
public function find($id)
{
$rowset = $this->select(array(‘id’ => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception(“OPS: There’s no product with id %s”, $id);
}
$product = new Entity\Product();
$hydrate = new Zend\Stdlib\Hydrator\ClassMethods(true);
$hydrate->hydrate($row->getArrayCopy(), $product);
return $product;
}
}
|
Usando a Depency Injection
Algo desconfortável que acontecerá com a instanciação de models será sempre capturar o adapter e passar via parametro no construtor. O pior seria se o construtor exigisse mais parâmetros, o que pode acontecer na sua aplicação. Para isso, a ZF2 disponibiliza um meio prático de criar nossos models sem nos preocuparmos com os parâmetros, que seria a dependency injection.
O uso de padrão de projeto ocorre no arquivo Module.php dentro da pasta do módulo sendo usado. Nele, há o método getServiceConfig(), onde retornaremos um vetor com a instância de nossos models dessa maneira:
public function getServiceConfig()
{
return array(
‘factories’ => array(
‘Application\Model\Grupo’ => function ($sm) {
$dbAdapter = $sm->get(‘Zend\Db\Adapter\Adapter’);
$model = new Model\Product($dbAdapter);
return $model;
},
)
);
}
|
A grande vantagem desse método é que nossas funções de criação de models podem exigir como parâmetro o ServiceManager, como você pode ver em function ($sm). Graças a esse detalhe, podemos capturar o que tiver sendo criado dentro do ServiceManager, inclusive o adapter.
Com isso podemos chamar nosso método no controller e executar operações.
class ExampleController extends AbstractActionController
{
$adapter = $this->getServiceLocator()->get(‘Zend\Db\Adapter\Adapter’);
$productModel = new ProductModel($adapter);
$productEntity = $productModel->find(‘12’);
$allProducts = $productModel->findAll();
/…
return array();
}
}
|