Zend Framework 2, making the model query a web service - php

I'm completely new to Zend framework (though I've already used Symfony2 and I've heard they're similar), and I've started a project in which I have to upgrade a site (that is already fully functional) that was created with Zend 1.11.
The aim of my upgrade is to allow data (that was originally stored in a database, and that will now be stored in nosql, and a database, and could be in the future stored elsewhere) to be more buildable and less strongly coupled with Zend's model. (Model as in the M of MVC).
In order to achieve this, I was asked to use a web service that would interact with the data, and Zend's model.
That way, when the data's structure would be modified, the Zend website wouldn't directly be impacted, (and would still work!) and we'd just have to re-arrange the web service.
Is there any elegant way to make Zend's model interact with a web service rather than a database?
I hope my question is understandable...
Have a nice day,
M.G.

You can use Data mapper pattern. As a reference, you can see how the module ZfcUser has adopted this pattern
You can create an interface of mapper for each entity and create an implementation according to the the data storage.
For example,
Product Entity
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set id.
*
* #param int $id
* #return UserInterface
*/
public function setId($id)
{
$this->id = (int) $id;
return $this;
}
/**
* Get name.
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set name.
*
* #param string $name
* #return UserInterface
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
}
Product Mapper
<?php
<?php
namespace Product\Mapper;
interface ProductMapperInterface
{
/**
* #var int $id
* #returns \Product\Entity\Product
*/
public function findById($id);
/**
* #var array $criteria
* #returns \Product\Entity\Product[]
*/
public function find(array $criteria, .....);
/**
* #var \Product\Entity\Product $product
*/
public function insert($product);
/**
* #var \Product\Entity\Product $product
*/
public function update($product);
}
To populate the entity, you can use hydrators. As a reference, you can view how ZfcUser uses hydrators.

Related

What's the best design for extra data on behaviour class?

I have a game where the player can finish some tasks.
I have separated the behaviour part of the task to its ORM part.
Eventually a copy of the task is being saved somewhere on the player's document (doesn't matter where for this specific question).
The problem is, I am not sure where to put the extra information that I send to the client that is not necessary for the behaviour itself, but it is needed to show the player information regarding the task itself.
This is my task interface:
interface ITask
{
/**
* #param Player $player
*/
public function init(Player $player);
/**
* #param PlayerAction $action
*/
public function progress(PlayerAction $action);
public function reset();
/**
* #return bool
*/
public function isComplete();
}
This is my abstract task:
abstract class BaseTask implements ITask
{
/**
* #var int
*/
public $id;
/**
* #var int
*/
protected $currentValue;
/**
* #var int
*/
protected $targetValue;
public function __construct($targetValue)
{
$this->currentValue = 0;
$this->targetValue = $targetValue;
}
/**
* #param int
*/
public abstract function setCurrentValue($current);
/**
* #return int
*/
public abstract function getCurrentValue();
/**
* #return int
*/
public abstract function getID();
/**
* #param int
*/
public abstract function setID($id);
/**
* #return int
*/
public abstract function getTargetValue();
/**
* #param int
*/
public abstract function setTargetValue($target);
/**
* #return boolean
*/
public function isComplete()
{
if ($this->getCurrentValue() >= $this->getTargetValue())
{
return true;
}
return false;
}
}
Now I need to decide how where to put the extra data, e.g description, title, theme etc...
I thought about two options: I can just put it on the base task
itself, but then what happens if I don't need it? I just leave it
blank? feel like the wrong place for me.
I could create a wrapper
class that will hold the task, but then I will need to always
call the wrapper to get to the task, and it feels kind of
wrong.
Looking for alternative suggestions.
You should inherit the CustomTask from TaskBase.
If you you have limitation in inheritance, encapsulate additional fields into a class called TaskAdditionalInfoBase and associate to the TaskBase.
Then various classes can inherit TaskAdditionalInfoBase to present a custom additional info to the the task.

Swagger Code Generation: models taking benefit of PHP7

We're using Swagger for our API specification and Swagger Code Generator to automatically generate the related models.
We're using the provided PHP models which works great but doesn't take benefit of PHP 7.1.
I tried to find PHP 7.1 models but I could not find any nor on the official repository or others people repositories.
Do you know some places where models that take benefit of PHP7 stand?
If not, our team is willing to do them. Some of you would be interested?
Current models, made for PHP5:
/**
* Figure.
*/
class Figure implements ArrayAccess {
/**
* #return int
*/
public function getId() {
return $this->container['id'];
}
/**
* #param int $id
*
* #return $this
*/
private function setId($id) {
$this->container['id'] = $id;
return $this;
}
}
Models taking benefit of PHP 7 would look like:
/**
* Figure.
*/
class Figure implements ArrayAccess {
public function getId(): int {
return $this->container['id'];
}
private function setId(int $id): self {
$this->container['id'] = $id;
return $this;
}
}

Doctrine trying to persist Owner

I've setup Doctrine and Symfony-forms independent of the Symfony Framework (as I don't need most of it).
The issue I'm having is, when trying to persist a new "Audit" which has an "Type" doctrine seems to want to persist the owning side of the relationship (Type).
For example as Audit may have a type of Vehicle Service.
// -- Model/Audit.php --
/**
* #var \Model\Type
*
* #ORM\ManyToOne(targetEntity="Model\Audit\Type", inversedBy="audits")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=true)
*/
private $type;
/**
* Set type
*
* #param \Model\Type $type
* #return Audit
*/
public function setType(\Model\Type $type)
{
$this->type = $type;
return $this;
}
And then in the inverse side:
/**
* #ORM\OneToMany(targetEntity="Model\Audit", mappedBy="type")
* #var type */
private $audits;
public function __construct() {
$this->audits = new \Doctrine\Common\Collections\ArrayCollection();
}
Persistance code looks as follows:
$data = $form->getData();
$entityManager->persist($data);
$entityManager->flush();
And finally the form class is:
class AuditType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name')
->add('type', 'entity', array(
'class' => "Model\Type"
));
}
All looks (to me at least) exactly the same as in all the documentations both Doctrine and Symfony sides but I'm getting this error:
A new entity was found through the relationship 'Model\Audit#type'
that was not configured to cascade persist operations for entity:
Vehicle Service. To solve this issue: Either explicitly call
EntityManager#persist() on this unknown entity or configure cascade
persist this association in the mapping for example
#ManyToOne(..,cascade={"persist"})."
Which is really frustrating as I don't want to persist the Type side, I just want to put (in most basic terms) the id of 3 into the type_id column. Yet Doctrine seems to think I want to create a new "Type" which I certainly do not. They already exist.
Using $entityManager->merge($audit); works in part, it allows the inital Audit and its FK's to be saved. However it caused any embedded forms to become ignored.
I think you need set
/**
* #ORM\OneToMany(targetEntity="Model\Audit", mappedBy="type")
* #var type
*/
private $audits;
public function __construct() {
$this->audits = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #return ArrayCollection
*/
public function getAudits()
{
return $this->audits;
}
/**
* #param Audit $audit
*/
public function addAudits(Audit $audit)
{
$this->audits->add($audit);
$audit->setTyoe($this);
}
and in Type Audit.model
// -- Model/Audit.php --
/**
* #var \Model\Type
*
* #ORM\ManyToOne(targetEntity="Model\Audit\Type", inversedBy="audits")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=true)
*/
private $type;
/**
* Set type
*
* #param \Model\Type $type
* #return Audit
*/
public function setType(\Model\Type $type)
{
$this->type = $type;
}

Traits and Their Usage

As of late I have exploring and expanding my PHP skills into new territory and working on getting a better understanding of various design patterns. From a technical standpoint I understand traits and how they are useful but don't have the practical side down just yet. There is a piece of code that I was working on recently, and I am not sure if this is the best way of accomplishing what I want.
<?php namespace Acme\Traits;
trait TCities{
/**
* #var self
*/
private static $instance;
/**
* #var int
*/
private $cityID;
/**
* #var float
*/
private $latitude;
/**
* #var float
*/
private $longitude;
/**
* #var
*/
private $cityName;
// more fields ----->>>
/**
* #return TCities
*/
public static function getInstance() {
if (!(self::$instance instanceof self)) {
self::$instance = new self;
}
return self::$instance;
}
/**
* #return int
*/
public function getCityID(): int{
return $this->cityID;
}
/**
* #param int $cityID
*/
public function setCityID(int $cityID){
$this->cityID = $cityID;
}
// more methods ....
Since I have city data that can come from 3 different databases and used all over the system, this makes it easy access that data without having to know all of the fields from the various DBs.
Here is an example of how the trait gets used.
<?php namespace Acme\QueryLibs\MySql;
use Acme\Traits\TCities;
class SqlLocationQueries extends SqlQuery{
use TCities;
/**
* SqlLocationQueries constructor.
*
* #param IDatabaseMySql $db
*/
public function __construct(IDatabaseMySql $db){
parent::__construct($db);
}
/**
* #param int $cityID
*
* #return TCities
*/
public function getCityData(int $cityID): TCities{
$query = ' SELECT *
FROM cities
WHERE id=?
LIMIT 1 ';
$params = [$cityID];
self::setQuery($query);
self::setParams($params);
$result = self::readOne();
if(empty($result)){
LocationErrors::unknownCityByID($cityID);
}
$cityInst = TCities::getInstance();
$cityInst->setCityID($result['cityID']);
$cityInst->setCityName($result['name']);
$cityInst->setLatitude($result['lat']);
$cityInst->setLongitude($result['lon']);
return $cityObj;
}
As you can see, this method returns 'TCities'. It's very convenient but not sure if this is the right/good approach?
Then in a higher level method (This is a very simplistic use case but gets the point across)
$locationOrigin = new SqlLocationQueries($this->db);
$cityOriginData = $locationOrigin->getCityData($cityID);
echo 'Welcome to '.$cityOriginData->getCityName().'<br />';
echo 'Where the coordinates are '.$cityOriginData->getLatitude().' '.$cityOriginData->getLatitude().'<br />';
Is this a misuse of traits or a good use case from them? If it is a bad use case, what would be the more efficient and correct way to accomplish this goal?

Netbeans Auto-Complete Not Working For Custom PHP Class

I've got the following class in a Zend Framework project:
<?php
/**
* User's class
*
* This class should be responsible for all
*
* #author Steve Davies
* #copyright 2012
* #version SVN: $Id$
*/
class Api_Admin_Users extends Api_Core
{
/**
* Class Constructor
*
* #return void
*/
public function __construct() {
parent::__construct();
}
/**
* Get User's name
*
* This returns the user's name
*
* #return void
*/
public function new() {
$user = self::_instance()->_em->getRepository('UserManagement\Users')->find('1');
echo $user->getFullName();
}
}
However when I try and use code hinting on $user->getFullName();, it doesn't work.
Using the following trick from here, it works:
/**
* Get User's name
*
* This returns the user's name
*
* #return void
*/
public function new() {
/* #var $user \UserManagement\Users */
$user = self::_instance()->_em->getRepository('UserManagement\Users')->find('1');
echo $user->getFullName();
}
But, I don't want to have to include that comment line everytime I instantiate the object. When I try to move this to the Class definition - or even the method definition, it fails to work.
Can anyone provide an answer for this?
PHP is a dynamic language and as such it is not trivial to infer variable types from static code analysis (like it is in Java for example).
It's especially difficult with factory methods like yours getRepository('UserManagement\Users').
NetBeans currently has no way of knowing how to translate the function argument to the type of returned variable (unless you're satisfied with some parent class from which all subclasses returned by that factory derive). Unfortunatelly vdoc's are the only way to deal with such cases.
Create a method in Api_Admin_Users to access the repository and add the type hint there. This will benefit all methods in the class. As long as the methods in the repository are type-hinted correctly, you're all set.
class Api_Admin_Users extends Api_Core
{
/**
* Class Constructor
*
* #return void
*/
public function __construct() {
parent::__construct();
}
/**
* Get the repository
*
* #return \UserManagement\Users
*/
public static function getRepository() {
return self::_instance()->_em->getRepository('UserManagement\Users');
}
/**
* Get User's name
*
* This returns the user's name
*
* #return void
*/
public function new() {
$user = self::getRepository()->find('1');
echo $user->getFullName();
}
}

Categories