Table joining using zend2 hydrate - orm

I have a form to insert users to DB, which needs to update two tables # once, user table and user_role_linker table. I am using hydrator (ClassMethod) to map my entities with the relative tables. I use two seperate entities, User and UserRole. The form contains all user fields that map with User table and user role field to map with user_role_linker table. When form submitted, bind the form with the User Entity.
$user = new \ZfcUser\Entity\User();
$form = $this->getRegisterForm();
$form->setHydrator($this->getFormHydrator());
$form->bind($user);
$user = $form->getData();
Inserting is perform in two steps.
$this->getUserMapper()->insert($user);
$user_role_linker->setId($user->getId());
$user_role_linker->setRoleId($data['user_role']);
$this->getUserRoleLinkMapper()->insert($user_role_linker);
First insert the user and return the user_id, then set user_id and user_role in UserRole entity and insert to table.
But now the problem is to fetch two table to one object or one array. I may need to perform a table join to get the solution. But there is no way I could find to join tables with using ClassMethod hydrator.
Please help me to accomplish this.
UserHydrator class
namespace ZfcUser\Mapper;
use Zend\Stdlib\Hydrator\ClassMethods;
use ZfcUser\Entity\UserInterface as UserEntityInterface;
class UserHydrator extends ClassMethods
{
/**
* Extract values from an object
*
* #param object $object
* #return array
* #throws Exception\InvalidArgumentException
*/
public function extract($object)
{
if (!$object instanceof UserEntityInterface) {
throw new Exception\InvalidArgumentException('$object must be an instance of ZfcUser\Entity\UserInterface');
}
/* #var $object UserInterface*/
$data = parent::extract($object);
$data = $this->mapField('id', 'user_id', $data);
//$data = $this->mapField('id', 'username', $data);
return $data;
}
/**
* Hydrate $object with the provided $data.
*
* #param array $data
* #param object $object
* #return UserInterface
* #throws Exception\InvalidArgumentException
*/
public function hydrate(array $data, $object)
{
if (!$object instanceof UserEntityInterface) {
throw new Exception\InvalidArgumentException('$object must be an instance of ZfcUser\Entity\UserInterface');
}
$data = $this->mapField('user_id', 'id', $data);
return parent::hydrate($data, $object);
}
protected function mapField($keyFrom, $keyTo, array $array)
{
$array[$keyTo] = $array[$keyFrom];
unset($array[$keyFrom]);
return $array;
}
}

Related

Laravel Excel cannot update records "WithUpserts" - error update

I face various issues while updating my existing table. I need to update the price field in my table, but I get an error in my code that I can't find. Even if I remove the sku it goes to the next "qty" field and outputs undefined. "Undefined index: sku"
<?php
namespace App\Imports;
use App\Models\CatalogStore;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithUpserts;
class CatalogupdateImport implements ToModel, WithUpserts
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new CatalogStore([
'sku' => $row['sku'],
'price' => $row['price'],
'qty' => $row['qty'],
]);
}
/**
* #return string|array
*/
public function uniqueBy()
{
return 'price';
}
}

Symfony 4 API Rest PUT : Map data to database entity

I'm a begginer in Symfony 4 and I'm developing an API Rest. I want to create a PUT resource that can handle many update cases.
In my case, I have a user with many properties but I will take an example with 3 properties to keep things simple :
User {
username,
email,
password
}
My PUT resource can be called in order to update Username, update Email or update Password. For example, to update Username, the user of my API will send a PUT request with only username :
{
username: "New username"
}
Same for email and password, he will only send the property he wants to change.
My problem is in my Controller, I have to do things like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(Request $request)
{
$userId = $request->get('id');
$user = $this->doctrine->getRepository(User::class)->findOneBy('id' => $userId);
$userFromRequest = $this->serializer->deserialize($request->getContent(), User::class, 'json');
if ($userFromRequest->getUsername() != NULL) {
$user->setUsername($userFromRequest->getUsername())
}
if ($userFromRequest->getEmail() != NULL) {
$user->setEmail($userFromRequest->getEmail())
}
if ($userFromRequest->getPassword() != NULL) {
$user->setPassword($userFromRequest->getPassword())
}
// ...
}
In my example I have only 3 properties, but imagine when I have 30 properties.
With Symfony 3 I used forms to validate / save my datas :
$form->submit($request->request->all(), $clearMissing);
Where $clearMissing is false to keep datas not provided by the user of my API. I can't find a way to do it with serializer but I guess I'm doing things wrong.
Thanks.
If I understand correctly, You can use the validator Component like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(User $user, Request $request, ValidatorInterface $validator)
{
$data = $request->getContent();
$this->serializer->deserialize($data, User::class, 'json', ['object_to_populate' => $user]);
//At this moment, the data you sent is merged with your user entity
/** #var ConstraintViolationList $errors */
$errors = $validator->validate($user, null ,['groups' => 'user_update']);
if (count($errors) > 0) {
//return json reponse with formated errors;
}
//if ok $entityManager->flush() and Response Json with serialization group;
...
}
In your user class :
class User implements UserInterface
{
/**
* #Assert\Email(groups={"user_create", "user_update"})
*/
private $email;
/**
* #Assert\NotBlank(groups={"user_create", "user_update"})
* #Assert\Length(min=7, groups={"user_create", "user_update"})
*/
private $password;
/**
* #Assert\Length(min=2, groups={"user_create", "user_update"} )
*/
private $username;
}
Related Validator component documentation : https://symfony.com/doc/current/validation/groups.html
You can also check this project : https://github.com/attineos/relation-deserializer

Phalcon DB Criteria in Model ORM

In Phalcon model is there a way to use SQL "IN" condition and other database criteria? For example, i want to execute this query in phalcon
SELECT user_fullname, user_email FROM tbl_users WHERE user_id IN (1,2,3,4,5)
I know you can use query builder but I want it to be ORM. I want a method in my model getUsersByIds($userIds, $columnsToSelect) that will accept an array of userids and columns that you want to fetch in the table.
See my model below
<?php
use Phalcon\Mvc\Model;
class User extends Model
{
/**
*
* #var integer
*/
public $user_id;
/**
*
* #var string
*/
public $user_email;
/**
*
* #var string
*/
public user_phone;
/**
*
* #var string
*/
public user_fullname;
public function initialize()
{ $this->hasMany('user_id','Store\Bag','user_id');
}
public function getSource()
{
return "tbl_user";
}
public function find($parameters = null)
{
return parent::find($parameters);
}
public function findFirst($parameters = null)
{
return parent::findFirst($parameters);
}
/**
* I want to execute this query in my model
* SELECT user_fullname,user_email from tbl_user where user_id IN (1,2,3,4,5);
*/
public function getUsersByIds($ids=array(), $columns=array())
{
if(!is_array($ids))
$ids = array($ids);
if(!is_array($columns))
$columns = array($columns);
...................................
.........................
..............
}
}
There are two ways of doing this:
Method 1: inWhere('columnName', array('columnValues'))
public function getUsersByIds(array $userIds, $columnsToSelect = '') {
return self::query()
->inWhere('id', $userIds)
->columns($columnsToSelect ?: '*')
->execute();
}
This one goes in your User model
Method 2: conditions => 'id IN ({ids:array})
User::find(['id IN ({ids:array})',
'columns' => $columnsToSelect,
'bind' => array('ids' => $userIds)]);
I am typing on my mobile, so there might be a typo in there.

Yii: change active record field names

I'm new to Yii and I have a table 'Student' with fields like 'stdStudentId', 'stdName', etc.
I'm making API, so this data should be returned in JSON. Now, because I want field names in JSON to just be like 'id', 'name', and I don't want all fields returned, i made a method in the model:
public function APIfindByPk($id){
$student = $this->findByPk($id);
return array(
'id'=>$student->stdStudentId,
'name'=>$student->stdName,
'school'=>$student->stdSchool
);
}
The problem is, stdSchool is a relation and in this situation, $student->stdSchool returns array with fields like schSchoolId, schName, etc. I don't want fields to be named like that in JSON, and also I don't want all the fields from School returned and I would like to add some fields of my own. Is there a way to do this in Yii, or I'll have to do it manually by writing methods like this?
I have been looking for the same thing. There is a great php lib named Fractal letting you achieve it: http://fractal.thephpleague.com/
To explain briefly the lib, for each of your models you create a Transformer that will be doing the mapping between your model attributes and the ones that need to be exposed using the api.
class BookTransformer extends Fractal\TransformerAbstract
{
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
];
}
}
In the transformer you can also set the relation that this model have :
class BookTransformer extends TransformerAbstract
{
/**
* List of resources relations that can be used
*
* #var array
*/
protected $availableEmbeds = [
'author'
];
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(Book $book)
{
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
];
}
/**
* Here we are embeding the author of the book
* using it's own transformer
*/
public function embedAuthor(Book $book)
{
$author = $book->author;
return $this->item($author, new AuthorTransformer);
}
}
So at the end you will call
$fractal = new Fractal\Manager();
$resource = new Fractal\Resource\Collection($books, new BookTransformer);
$json = $fractal->createData($resource)->toJson();
It's not easy to describe all the potential of fractal in one answer but you really should give it a try.
I'm using it along with Yii so if you have some question don't hesitate!
Since you are getting the values from the database using Yii active record, ask the database to use column aliases.
Normal SQL would be something like the following :
SELECT id AS Student_Number, name AS Student_Name, school AS School_Attending FROM student;
In Yii, you can apply Criteria to the findByPK() function. See here for reference : http://www.yiiframework.com/doc/api/1.1/CActiveRecord#findByPk-detail
$criteria = new CDbCriteria();
$criteria->select = 'id AS Student_Number';
$student = Student::model()->findByPk($id, $criteria);
Note that in order to use a column alias like that, you will have to define a virtual attribute Student_Number in your Student{} model.
Override the populateRecord() function of ActiveRecord can achieve this!
My DishType has 5 properties and override the populateRecord function Yii would invoke this when records fetched from db.
My code is here!
class DishType extends ActiveRecord
{
public $id;
public $name;
public $sort;
public $createTime;
public $updateTime;
public static function populateRecord($record, $row)
{
$pattern = ['id' => 'id', 'name' => 'name', 'sort' => 'sort', 'created_at' => 'createTime', 'updated_at' => 'updateTime'];
$columns = static::getTableSchema()->columns;
foreach ($row as $name => $value) {
$propertyName = $pattern[$name];
if (isset($pattern[$name]) && isset($columns[$name])) {
$record[$propertyName] = $columns[$name]->phpTypecast($value);
}
}
parent::populateRecord($record, $row);
}
}

Extra Attribute Disappears after it is set successfully

I am trying to get all records that are tied to a parent object through a lookup table, and insert them directly into the model. I have an object, Role, that hasMany() RoleEndpoints. RoleEndpoints belongs to Role and hasMany() Endpoints. All the data is being retrieved exactly as I expect, however, it seems to disappear after I set it.
<?php
class ACL {
private $_di;
public function __construct($di) {
$this->_di = $di;
}
public function createACL() {
if(!$this->_acl) {
$this->_acl = new stdClass();
$roles = $possibleRoles = Roles::find();
/**
* Check if there is at least one role out there
*/
if($roles->count() > 0) {
/**
* Iterate over all of the records
*/
while($roles->valid()) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach($roles->current()->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
I tried several different approaches; this seemed the best
*/
$roles->current()->endpoints = $endpoints;
}
/**
* Set object to loop through from the beginning
*/
$roles->rewind();
/**
* Here is where my issue lies.
*
* The endpoints attribute, which is set as a public attribute in the model class
* gets unset for some reason
*/
while($roles->valid()) {
echo '<pre>';
var_dump($roles->current());
exit;
}
As the comments say, during the second iteration of the result set, the endpoints attribute drops becomes null for some reason. Am I doing something wrong here? Am I missing a step?
Any help would be appreciated. Thank you!
There is a missing next() in the iterator traversing:
while ($roles->valid()) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach ($roles->current()->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
* I tried several different approaches; this seemed the best
*/
$roles->current()->endpoints = $endpoints;
//Missing next
$roles->next();
}
Also, you don't need to iterate the cursor in that way, just a foreach is easy to read and maintain:
$roles = Roles::find();
$roleEndpoints = array();
if (count($roles)) {
foreach ($roles as $role) {
$endpoints = array();
/**
* Grab each role's endpoints through the relationship
*/
foreach ($role->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
/**
* At this point, the endpoints exist in the current Role model;
* I tried several different approaches; this seemed the best
*/
$roleEndpoints[$role->id] = $endpoints;
}
}
//Get the endpoints
foreach ($roleEndpoints as $roleId => $endpoint) {
//...
}
Also, If this is a common task you can add a method to your model to reuse that logic:
class Roles extends Phalcon\Mvc\Model
{
public function getEndpoints()
{
$endpoints = array();
foreach ($this->getRoleEndpoints() as $roleEndpoint) {
$endpoints[] = Endpoints::findFirst($roleEndpoint->endpoint_id);
}
return $endpoints;
}
public function initialize()
{
//...
}
}
So you can get your endpoints:
$roles = Roles::find();
if (count($roles)) {
foreach ($roles as $role) {
$endpoints = $role->getEndpoints();
}
}