I have a lumen application where I need to store incoming JSON Request. If I write a code like this:
public function store(Request $request)
{
if ($request->isJson())
{
$data = $request->all();
$transaction = new Transaction();
if (array_key_exists('amount', $data))
$transaction->amount = $data['amount'];
if (array_key_exists('typology', $data))
$transaction->typology = $data['typology'];
$result = $transaction->isValid();
if($result === TRUE )
{
$transaction->save();
return $this->response->created();
}
return $this->response->errorBadRequest($result);
}
return $this->response->errorBadRequest();
}
It works perfectly. But use Request in that mode is boring because I have to check every input field to insert them to my model. Is there a fast way to send request to model?
You can do mass assignment to Eloquent models, but you need to first set the fields on your model that you want to allow to be mass assignable. In your model, set your $fillable array:
class Transaction extends Model {
protected $fillable = ['amount', 'typology'];
}
This will allow the amount and typology to be mass assignable. What this means is that you can assign them through the methods that accept arrays (such as the constructor, or the fill() method).
An example using the constructor:
$data = $request->all();
$transaction = new Transaction($data);
$result = $transaction->isValid();
An example using fill():
$data = $request->all();
$transaction = new Transaction();
$transaction->fill($data);
$result = $transaction->isValid();
You can either use fill method or the constructor. First you must include all mass assignable properties in fillable property of your model
Method 1 (Use constructor)
$transaction = new Transaction($request->all());
Method 2 (Use fill method)
$transaction = new Transaction();
$transaction->fill($request->all());
Create your TransactionRequest with rules extends FormRequest
public function store(TransactionRequest $request)
{
$transaction = new Transaction($request->validated());
$transaction->save();
}
Related
I'm new to Laravel and I want to insert data into the database.
class Test1Controller extends Controller {
public function index(Request $req)
{
$item = $req->input('item');
$name = $req->input('name');
$data=array('item'=>$item,'name'=>$name);
DB::table('test1')->insert($data)
$view = view('common.test1');
$obj = DB::table('test1')->get();
$view->obj = $obj;
return $view;
}
and this is my route file code
Route::post('/test/test1/index/','Controller#index');
I got an error
Call to undefined method Illuminate\Support\Facades\Request::input()
try to include this library at the top of your controller
use Illuminate\Http\Request;
I have two entities that I'm trying to apply a OneToMany / ManyToOne relationship to (one Game has many GameContent).
Game
/**
* #ORM\OneToMany(targetEntity="GameContent", mappedBy="game")
*/
private $contents;
public function __construct()
{
$this->contents = new ArrayCollection();
}
public function getContents()
{
return $this->contents;
}
GameContent
/**
* #ORM\ManyToOne(targetEntity="Game", inversedBy="contents")
*/
private $game;
And the following code inserts both records into their respective tables:
$game = $form->getData();
$content = new GameContent();
$content->setType('some type');
$game->getContents()->add($content);
$em = $this->getDoctrine()->getManager();
$em->persist($content);
$em->persist($game);
$em->flush();
However, the GameContent's game_id is inserted as null:
INSERT INTO game_content (type, game_id) VALUES (?, ?)
Parameters: { 1: 'some type', 2: null }
I've also tried:
changing the order of persist()
replacing $game->getContents()->add($content) with $game->addContents($content) by doing $this->contents[] = $content;
removing persist($content) and having cascade={"persist"} on the Game entity.
Why is game_id being inserted as null?
My current workaround is:
$em = $this->getDoctrine()->getManager();
$game = $form->getData();
$em->persist($game);
$content = new GameContent();
$content->setType('some type');
$content->setGame($game);
$em->persist($content);
$em->flush();
You have 2 solutions :
Persist children in controller
Without cascade={"persist"}
$em = $this->getDoctrine()->getManager();
// Get data
$game = $form->getData();
// Create new GameContent and hydrate
$content = new GameContent();
$content->setType('some type');
// Associate Game <> GameContent
$content->setGame($game);
// Persist GameContent
$em->persist($content);
// Persist Game and commit
$em->persist($game);
$em->flush();
Persist children in cascade
With cascade={"persist"} in OneToMany relation.
Add in setGame() function, to force association :
$game->addContent($this);
And remove persist :
$em = $this->getDoctrine()->getManager();
// Get data
$game = $form->getData();
// Create new GameContent and hydrate
$content = new GameContent();
$content->setType('some type');
// Associate Game <> GameContent
$content->setGame($game);
// Persist Game and commit
$em->persist($game);
$em->flush();
I think the error was also due to the positioning of the persist on game.
Add in setGame() function, to force association :
$game->addContent($this);
And remove persist :
$em = $this->getDoctrine()->getManager();
// Get data
$game = $form->getData();
// Create new GameContent and hydrate
$content = new GameContent();
$content->setType('some type');
// Associate Game <> GameContent
$content->setGame($game);
// Persist Game and commit
$em->persist($game);
$em->flush();
Note that today (Doctrine 2.7.1), the make:entity utility creates methods that does this stuff for you, in your case you would have had a method like this on your Game entity:
public function addContent(GameContent $content): self
{
$this->contents->add($content);
$content->setGame($this); // <-- IMPORTANT PART IS HERE
return $this;
}
Then, calling this on the game would have done the job:
this->addContent((new GameContent())->setType('some type'));
Further to the accepted answer, my next step was to create a form to handle the GameContent data, which led to further changes and some simplified logic.
I now setGame() in Game::addContent(), and so I've removed $game->addContent($this); in GameContent::setGame().
Game
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="GameContent", mappedBy="game", cascade={"persist"})
*/
private $contents;
public function __construct()
{
$this->contents = new ArrayCollection();
}
public function getContents()
{
return $this->contents;
}
public function addContent(GameContent $content)
{
$this->contents->add($content);
$content->setGame($this);
return $this;
}
public function removeContent(GameContent $content)
{
$this->contents->removeElement($content);
return $this;
}
GameContent
/**
* #ORM\ManyToOne(targetEntity="Game", inversedBy="contents")
*/
private $game;
public function setGame(Game $game)
{
$this->game = $game;
return $this;
}
/**
* #return Game
*/
public function getGame()
{
return $this->game;
}
The real-world form-handling logic will look like this:
$game = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($game);
$em->flush();
More information at: http://symfony.com/doc/2.8/form/form_collections.html (see Doctrine: Cascading Relations and saving the "Inverse" side).
I need some help with SF 2.7 serializer
I have made an API with get Json Data like this :
{
"dateDebut":"2017-02-16",
"dateFin":"2018-02-16",
"caMoisTotalHorsSessions":"5.2",
"caMoisClients":"5.3",
"caMoisGarantie":"5.4",
"caMoisHuile":"5.5" }
I tried many way in order to deserialze into my object Class where dateDebut and dateFin are attending to be Datetime object and not string
try {
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$callback = function ($date) {
return new \DateTime($date);
};
$normalizer->setCallbacks(array(
'dateDebut' => $callback,
'dateFin' => $callback, ));
$serializer = new Serializer(array($normalizer), array($encoder));
$entity = $serializer->deserialize($request->getContent(), $class, $format);
} catch (RuntimeException $e) {
return new JsonResponse(
['code' => Response::HTTP_BAD_REQUEST, 'message' => $this->trans('api.message.data_error')],
Response::HTTP_BAD_REQUEST);
}
But callbacks are never used :/ Could anyone help me please ?
Aim is to transform date string into Datetime object automatically before flush the object in database.
Thanks a lot
What you are trying to do is denormalization. The normalizer callbacks are for normalization. I think it's pretty confusing. It's strange that they would offer setting callback for just one direction.
I tested some code doing what I think you want to do. You need a custom normalizer class. The class is not so complicated, it can extend from the GetSetNormalizer or the ObjectNormalizer. You just want to create the \DateTime inside here, and you might add some validation for the date time.
class BoardNormalizer extends GetSetMethodNormalizer
{
public function denormalize($data, $class, $format = null, array $context = array())
{
if (isset($data['created'])) {
$data['created'] = new \DateTime($data['created']);
}
return parent::denormalize($data, $class, $format, $context);
}
}
I tested it with this code:
$json = json_encode([
'created' => '2017-02-20T05:49:51-0500'
]);
$encoder = new JsonEncoder();
$normalizer = new MyCustomNormalizer();
$serializer = new Serializer([$normalizer], [$encoder]);
$entity = $serializer->deserialize($json, MyCustomClass::class, 'json');
And it produced my custom class where the created property was a \DateTime object.
Aim is to transform date string into Datetime object automatically before flush the object in database.
Something like this? Using setters/getters ? I'm using the following code in entity
private $created;
public function setCreated($created)
{
if (!($created instanceof \DateTime)) {
$created = date_create($created);
}
$this->created = $created;
return $this;
}
I am using Doctrine 2 and Zend framework since a few days.
I am generating my entities across yaml files.
Now I met an issue to convert my entities Doctrine into Json format (in order to use it through AJAX).
Here is the code used :
$doctrineobject = $this->entityManager->getRepository('\Entity\MasterProduct')->find($this->_request->id);
$serializer = new \Symfony\Component\Serializer\Serializer(array(new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer()), array('json' => new Symfony\Component\Serializer\Encoder\JsonEncoder()));
$reports = $serializer->serialize($doctrineobject, 'json');
below is the return I get :
Fatal error: Maximum function nesting level of '100' reached, aborting! in /Users/Sites/library/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php on line 185
the issue seems to be the same than here :
http://comments.gmane.org/gmane.comp.php.symfony.symfony2/2659
but there is not proper solution proposed.
Any idea how can I do it ?
Cheers
I solved the same problem by writing my own GetSetNormalizer my class. Defined static variable in a class for branching
class LimitedRecursiveGetSetMethodNormalizer extends GetSetMethodNormalizer
{
public static $limit=2;
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null)
{
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = strtolower(substr($method->name, 3));
$attributeValue = $method->invoke($object);
if (null !== $attributeValue && !is_scalar($attributeValue) && LimitedRecursiveGetSetMethodNormalizer::$limit>0) {
LimitedRecursiveGetSetMethodNormalizer::$limit--;
$attributeValue = $this->serializer->normalize($attributeValue, $format);
LimitedRecursiveGetSetMethodNormalizer::$limit++;
}
$attributes[$attributeName] = $attributeValue;
}
}
return $attributes;
}
/**
* Checks if a method's name is get.* and can be called without parameters.
*
* #param ReflectionMethod $method the method to check
* #return Boolean whether the method is a getter.
*/
private function isGetMethod(\ReflectionMethod $method)
{
return (
0 === strpos($method->name, 'get') &&
3 < strlen($method->name) &&
0 === $method->getNumberOfRequiredParameters()
);
}
}
And usage
LimitedRecursiveGetSetMethodNormalizer::$limit=3;
$serializer = new Serializer(array(new LimitedRecursiveGetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$response =new Response($serializer->serialize($YOUR_OBJECT,'json'));
JMSSerializerBundle seems to handle circular references fine.
I have an entity called User which has inheritance for Student, Professional and Business.
When a user is registered, is only a User but they must update their profile and choose which kind of user is, I have a form which handles this, a controller which gets the form data, but I can't update the discriminator field type with $userEntity->setType()
This is my mapping stuff
class User
{
const TYPE_BASIC = "Basico";
const TYPE_STUDENT = "Estudiante";
const TYPE_PROFESSIONAL = "Profesional";
const TYPE_BUSINESS = "Empresa";
protected $type = self::TYPE_BASIC;
public function getType()
{
return self::TYPE_BASIC;
}
public function setType($type)
{
$this->type = $type;
}
class Student extends User
{
protected $type = self::TYPE_STUDENT;
And then Professional and Business just like Student (changing const)
<entity name="User" table="user_base" inheritance-type="JOINED">
<discriminator-column name="type" type="string"/>
<discriminator-map>
<discriminator-mapping value="Basico" class="User"/>
<discriminator-mapping value="Estudiante" class="Student"/>
<discriminator-mapping value="Profesional" class="Professional"/>
<discriminator-mapping value="Empresa" class="Business"/>
</discriminator-map>
the child tables are named user_xxx where xxx = Student/Professional/Business
And this is my controller
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$data = $form->all();
$type = $data['type']->getData();
$email = $data['email']->getData();
$profile = $data['profile']->all();
$name = $profile['name']->getData();
$lastName = $profile['lastName']->getData();
$birth = $profile['birth']->getData();
$profileEntity = new Profile();
$profileEntity->setBirth($birth);
$profileEntity->setName($name);
$profileEntity->setLastName($lastName);
$profileEntity->setUser($user);
$em->persist($profileEntity);
ladybug_dump($type);
$userEntity = $em->getRepository('User')->find($user);
$userEntity->setProfile($profileEntity);
$userEntity->setType($type);
if($user->getEmail() != $email)
$userEntity->setEmail($email);
$em->persist($userEntity);
$em->flush();
}
Everything is persisted but type field, which remains it's original data. I know when I change discriminator column I need to create a new row inside it's child element, but first I want to know how to change the discriminator column.
it is possible if you use this custom bit of code in the Form of a Trait which you can use inside a Repository.
The Trait:
namespace App\Doctrine\Repository;
use App\Exception\InvalidDiscriminatorClassException;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Discriminator Trait
*/
trait DiscriminatorTrait
{
/**
* #return ClassMetadata
*/
abstract public function getClassMetadata();
/**
* #return EntityManager
*/
abstract public function getEntityManager();
/**
* Update Discriminator Column
*
* #param integer $id
* #param string $class
* #return boolean
* #throws InvalidDiscriminatorClassException
*/
private function updateDiscriminatorColumn($id, $class)
{
/* #var ClassMetadata $classMetadata */
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new InvalidDiscriminatorClassException($class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
/* #var Connection $connection */
$connection = $this->getEntityManager()->getConnection();
try {
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
catch (DBALException $e) {
return false;
}
return true;
}
}
According to the Doctrine documentation on Inheritance mapping, it is not possible to either get or set the type. You may wish take advantage of PUGXMultiUserBundle, which readily handles the mapping. This bundle also makes it possible for your users to register with the appropriate profile.