Symfony3 - Error with my method getDomain() - php

I have a problem with my method getDomain() becuase I got report ,,Call to a member function getDomain() on null''
This is my Entity ....
class Clinic
{
.............
/**
* #ORM\Column(type="string")
*/
private $domain;
..............
/**
* #return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* #param string $domain
*/
public function setDomain($domain)
{
$this->domain = $domain;
}
...............
And this is my EventListener
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$currentHost = $request->getHttpHost();
$c = $this->em
->getRepository('Mybundle:Clinic')
->findOneBy(['domain' => $currentHost]);
if (!$c) {
$c = $this->em
->getRepository('Mybundle:Clinic')
->findOneBy(['domain' => $this->baseHost]);
}
$this->router->getContext()->setParameter('_domain', $c->getDomain());
$this->cManager->setCC($c);
I have date in my database, please help me.

If $c is setted in your first call, variable called $clinic is never setted, but used in router call.

Related

With doctrine ODM, can I embed many subdocuments in a main document?

I try to save this JSON:
{
"vendorId": "vendor-fc162cdffd73",
"company": {
"companyId": "bcos1.company.1806cf97-a756-4fbf-9081-fc162cdffd73",
"companyVersion": 1,
"companyName": "Delivery Inc.",
"address": {
"streetAddress": "300 Boren Ave",
"city": "Seattle",
"region": "US-WA",
"country": "US",
"postalCode": "98109",
"storeName": "Seattle Store",
"coordinates": {
"latitude": "45.992820",
"longitude": "45.992820"
}
},
"emailAddress": "johndoe#amazon.com",
"phoneNumber": "1234567890",
"websiteUrl": "delivery.com",
"creationDate": "2022-03-06T21:00:52.222Z"
},
"creationDate": "2022-04-06T21:00:52.222Z"
}
Company is a subdocument this has address and address has coordinates subdocument.
When I try to save with Hydratation, see example:
https://www.doctrine-project.org/projects/doctrine-laminas-hydrator/en/3.0/basic-usage.html#example-4-embedded-entities
I got this error:
1) AppTest\Services\AccountsServiceTest::testNewAccount with data set #0 (array('{"companyId":"bcos1.com...222Z"}', '', ''))
array_flip(): Can only flip STRING and INTEGER values!
vendor/doctrine/doctrine-laminas-hydrator/src/DoctrineObject.php:488
vendor/doctrine/doctrine-laminas-hydrator/src/DoctrineObject.php:355
vendor/doctrine/doctrine-laminas-hydrator/src/DoctrineObject.php:165
src/App/Document/Repository/AccountRepository.php:67
In DoctrineObject line 488
protected function toOne(string $target, $value): ?object
{
$metadata = $this->objectManager->getClassMetadata($target);
if (is_array($value) && array_keys($value) !== $metadata->getIdentifier()) {
// $value is most likely an array of fieldset data
$identifiers = array_intersect_key(
$value,
array_flip($metadata->getIdentifier())
);
$object = $this->find($identifiers, $target) ?: new $target();
return $this->hydrate($value, $object);
}
return $this->find($value, $target);
}
My code:
$vendorAccountId = uniqid('vendor-account-id-');
$account = new Account();
$hydrator->hydrate($data, $account);
My main Entity:
<?php
namespace App\Document\Entity;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(db="awesome-company", collection="Account", repositoryClass="App\Document\Repository\AccountRepository")
*/
class Account
{
/** #MongoDB\Id(name="_id") */
private string $id;
/** #MongoDB\Field(type="string", name="vendorAccountId") */
private string $vendorAccountId;
/**
* #return string
*/
public function getVendorAccountId(): string
{
return $this->vendorAccountId;
}
/**
* #param string $vendorAccountId
*/
public function setVendorAccountId(string $vendorAccountId): void
{
$this->vendorAccountId = $vendorAccountId;
}
/**
* #MongoDB\EmbedOne(targetDocument=Company::class)
*/
private Company $company;
/**
* #MongoDB\Field(type="string",name="realm")
**/
private $realm;
/**
* #MongoDB\Field(type="string",name="domain")
**/
private $domain;
/**
* #MongoDB\Field(type="date",name="created_at")
**/
private \DateTime $createdAt;
public function __construct()
{
$this->company = new Company();
$this->createdAt = new \DateTime();
}
/**
* #return mixed
*/
public function getCompany()
{
return $this->company;
}
/**
* #param mixed $company
*/
public function setCompany($company): void
{
$this->company = $company;
}
/**
* #return mixed
*/
public function getRealm()
{
return $this->realm;
}
/**
* #param mixed $realm
*/
public function setRealm($realm): void
{
$this->realm = $realm;
}
/**
* #return mixed
*/
public function getDomain()
{
return $this->domain;
}
/**
* #param mixed $domain
*/
public function setDomain($domain): void
{
$this->domain = $domain;
}
/**
* #return \DateTime
*/
public function getCreatedAt(): \DateTime
{
return $this->createdAt;
}
/**
* #return string
*/
public function getId(): string
{
return $this->id;
}
/**
* #param string $id
*/
public function setId(string $id): void
{
$this->id = $id;
}
}
Company embed document:
<?php
namespace App\Document\Entity;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** #MongoDB\EmbeddedDocument * */
class Company
{
/**
* #MongoDB\Field(type="string",name="company_id")
**/
private string $companyId;
/**
* #MongoDB\Field(type="int",name="company_version")
**/
private int $companyVersion;
/**
* #MongoDB\Field(type="string",name="company_name")
**/
private string $companyName;
/**
* #MongoDB\EmbedOne(targetDocument=Address::class)
*/
private Address $address;
/**
* #MongoDB\Field(type="string",name="email_address")
**/
private string $emailAddress;
/**
* #MongoDB\Field(type="string",name="phone_number")
**/
private string $phoneNumber;
/**
* #MongoDB\Field(type="string",name="website_url")
**/
private string $websiteUrl;
/**
* #MongoDB\Field(type="date",name="creation_date")
**/
private \DateTime $creationDate;
public function __construct()
{
$this->address = new Address();
}
public function getCompanyId(): string
{
return $this->companyId;
}
public function setCompanyId($companyId)
{
$this->companyId = $companyId;
}
public function getCompanyVersion(): int
{
return $this->companyVersion;
}
public function setCompanyVersion($companyVersion)
{
$this->companyVersion = $companyVersion;
}
public function getCreationDate(): \DateTime
{
return $this->creationDate;
}
public function setCreationDate($creationDate)
{
$this->creationDate = $creationDate;
}
public function getWebsiteUrl(): string
{
return $this->websiteUrl;
}
public function setWebsiteUrl($websiteUrl)
{
$this->websiteUrl = $websiteUrl;
}
public function getPhoneNumber(): string
{
return $this->phoneNumber;
}
public function setPhoneNumber($phoneNumber)
{
$this->phoneNumber = $phoneNumber;
}
public function getEmailAddress(): string
{
return $this->emailAddress;
}
public function setEmailAddress($emailAddress)
{
$this->emailAddress = $emailAddress;
}
public function getAddress(): Address
{
return $this->address;
}
public function setAddress(Address $address)
{
$this->address = $address;
}
public function getCompanyName(): string
{
return $this->companyName;
}
public function setCompanyName($companyName)
{
$this->companyName = $companyName;
}
}
What you're trying to do is perfectly fine from ODM's perspective - you can have as many embeddables deep as you want. Unless there is something fishy going on in your Address or Coordinates embedded documents I would expect a bug in the laminas-hydrator package. The fact that ORM does not allow nested embeddables makes this scenario more likely. Best would be to try creating a failing test case and send a pull request with it.
In the meantime you can leverage ODM's hydrator:
$hydrator = $this->dm->getHydratorFactory()->getHydratorFor(Account::class);
$hydrator->hydrate(new Account(), $data, [Query::HINT_READ_ONLY => true]);
Please note the Query::HINT_READ_ONLY passed as a 3rd argument to hydrate. With this hint ODM will not mark hydrated objects as managed which is what you need for later insertion. Hydrated EmbedOne objects should be good to go without the hint, but EmbedMany may not work correctly without it. Please see https://github.com/doctrine/mongodb-odm/issues/1377 and https://github.com/doctrine/mongodb-odm/pull/1403 for more details about said hint.
Thank you #malarzm. I do this:
$coordinates = new Coordinates();
$hydrator->hydrate($data['company']['address']['coordinates'], $coordinates);
unset($data['company']['address']['coordinates']);
$address = new Address();
$hydrator->hydrate($data['company']['address'], $address);
unset($data['company']['address']);
$company = new Company();
$hydrator->hydrate($data['company'], $company);
unset($data['company']);
$account = new Account();
$hydrator->hydrate($data, $account);
$address->setCoordinates($coordinates);
$company->setAddress($address);
$account->setCompany($company);
I realized unset() for each embed sud-document and finally I set each sub-document with set method of my entity.
It was the only way to do work fine.

How to properly manage Symfony translations from database

I am actually working on a Symfony 4.4 project, on which I need to be able to update Twig template translations from an admin interface.
This is the first time I try to implement a database based translation system, so I followed this tutorial: https://medium.com/#andrew72ru/store-translation-messages-in-database-in-symfony-3f12e579df74
At this time, I made a simple class which loads a MessageCatalogue from my database:
<?php
namespace App\Service;
use App\Entity\Translation;
use App\Repository\TranslationRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
class DatabaseTranslationManager implements LoaderInterface
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $doctrine)
{
$this->entityManager = $doctrine;
}
/**
* #param mixed $resource
* #param string $locale
* #param string $domain
*/
public function load($resource, $locale, $domain = 'messages'): MessageCatalogue
{
$translationRepository = $this->getRepository();
if (false === $translationRepository instanceof TranslationRepository) {
return new MessageCatalogue($locale);
}
$messages = $translationRepository->findByDomainAndLocale($domain, $locale);
$values = array_map(
static function (Translation $entity) {
return $entity->getTranslation();
},
$messages
);
return new MessageCatalogue(
$locale, [
$domain => $values,
]
);
}
/**
* #return ObjectRepository<Translation>
*/
public function getRepository(): ObjectRepository
{
return $this->entityManager->getRepository(Translation::class);
}
}
Which is registered in my services:
translation.loader.db:
class: App\Service\DatabaseTranslationManager
arguments:
- '#doctrine.orm.entity_manager'
tags:
- { name: translation.loader, alias: db }
The associated Translation entity is quite straightforward:
<?php
namespace App\Entity;
use App\Repository\TranslationRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=TranslationRepository::class)
*/
class Translation
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* #ORM\Column(type="string", length=255)
*/
private string $domain;
/**
* #ORM\Column(type="string", length=2)
*/
private string $locale;
/**
* #ORM\Column(type="string", length=255)
*/
private string $key;
/**
* #ORM\Column(type="text")
*/
private string $translation;
public function __construct(string $domain, string $locale, string $key, string $translation)
{
$this->domain = $domain;
$this->locale = $locale;
$this->key = $key;
$this->translation = $translation;
}
public function getId(): ?int
{
return $this->id;
}
public function getDomain(): string
{
return $this->domain;
}
public function setDomain(string $domain): self
{
$this->domain = $domain;
return $this;
}
public function getLocale(): string
{
return $this->locale;
}
public function setLocale(string $locale): self
{
$this->locale = $locale;
return $this;
}
public function getKey(): string
{
return $this->key;
}
public function setKey(string $key): self
{
$this->key = $key;
return $this;
}
public function getTranslation(): string
{
return $this->translation;
}
public function setTranslation(string $translation): self
{
$this->translation = $translation;
return $this;
}
}
It works quite well at this point, but I'm still facing some issues:
I need to create empty <domain>.<locale>.db files so that the custom translation loader is triggered, which is ok but not ideal
it looks like I need to clear the Symfony cache each time I change a translation in the database. This is an issue as I need translations to be updated anytime by admin users with no access to the console
I will lose all my translations each time I reset the database. The best option for me would be to load my <domain>.<locale>.yaml files as a fallback if a translation does not exist in the database.
As I could not find any better solution at this time, I'm looking for recommendations on how to achieve such a translation system.

Zend Expressive API does not return contents of objects

I'm creating a small API, mostly for learning purposes, but, I might implement it into a project I'm working on. So far, I have installed the zend expressive skeleton application and set up my models and entities. I'm able to query the database and get results, but, when I return the results as a JSON Response, I can only see a list of empty arrays for each result. I would like to be able to return the actual objects that are being returned from the database instead of converting them to arrays.
HomePageHandler.php
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Entity\Product;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Expressive\Router;
use Zend\Expressive\Template\TemplateRendererInterface;
use App\Model\ProductModel;
class HomePageHandler implements RequestHandlerInterface
{
/** #var string */
private $containerName;
/** #var Router\RouterInterface */
private $router;
/** #var null|TemplateRendererInterface */
private $template;
private $productModel;
public function __construct(
string $containerName,
Router\RouterInterface $router,
?TemplateRendererInterface $template = null,
ProductModel $productModel
) {
$this->containerName = $containerName;
$this->router = $router;
$this->template = $template;
$this->productModel = $productModel;
}
public function handle(ServerRequestInterface $request) : ResponseInterface
{
$data = $this->productModel->fetchAllProducts();
return new JsonResponse([$data]);
//return new HtmlResponse($this->template->render('app::home-page', $data));
}
}
I'm expecting a JSON Response returned with a list of 18 "Product" entities. My results look like.
[[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]]
Let me know if there is any other code you would like to see.
Thanks in advance!
Edited with Product.php code
<?php
/**
* Created by PhpStorm.
* User: Brock H. Caldwell
* Date: 3/14/2019
* Time: 4:04 PM
*/
namespace App\Entity;
class Product
{
protected $id;
protected $picture;
protected $shortDescription;
protected $longDescription;
protected $dimensions;
protected $price;
protected $sold;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return mixed
*/
public function getPicture()
{
return $this->picture;
}
/**
* #param mixed $picture
*/
public function setPicture($picture)
{
$this->picture = $picture;
}
/**
* #return mixed
*/
public function getShortDescription()
{
return $this->shortDescription;
}
/**
* #param mixed $shortDescription
*/
public function setShortDescription($shortDescription)
{
$this->shortDescription = $shortDescription;
}
/**
* #return mixed
*/
public function getLongDescription()
{
return $this->longDescription;
}
/**
* #param mixed $longDescription
*/
public function setLongDescription($longDescription)
{
$this->longDescription = $longDescription;
}
/**
* #return mixed
*/
public function getDimensions()
{
return $this->dimensions;
}
/**
* #param mixed $dimensions
*/
public function setDimensions($dimensions)
{
$this->dimensions = $dimensions;
}
/**
* #return mixed
*/
public function getPrice()
{
return $this->price;
}
/**
* #param mixed $price
*/
public function setPrice($price)
{
$this->price = $price;
}
/**
* #return mixed
*/
public function getSold()
{
return $this->sold;
}
/**
* #param mixed $sold
*/
public function setSold($sold)
{
$this->sold = $sold;
}
public function exchangeArray($data)
{
$this->id = (!empty($data['id'])) ? $data['id'] : null;
$this->picture = (!empty($data['picture'])) ? $data['picture'] : null;
$this->shortDescription = (!empty($data['shortDescription'])) ? $data['shortDescription'] : null;
$this->longDescription = (!empty($data['longDescription'])) ? $data['longDescription'] : null;
$this->dimensions = (!empty($data['dimensions'])) ? $data['dimensions'] : null;
$this->price = (!empty($data['price'])) ? $data['price'] : null;
$this->sold = (!empty($data['sold'])) ? $data['sold'] : null;
}
}
You need to either make the properties public, or implement the JsonSerializable interface in your Product entity. All of its properties are protected, which is fine, but that means they aren't exposed when the object is JSON encoded.
Here are some brief examples:
class Example1 { protected $a = 1; }
class Example2 { public $b = 2; }
class Example3 implements JsonSerializable {
protected $c = 3;
public function jsonSerialize() {
return ['c' => $this->c];
}
}
echo json_encode([new Example1, new Example2, new Example3]);
The result:
[{},{"b":2},{"c":3}]
If you choose to implement JsonSerializable, exactly how you do it is up to you, but you just need a jsonSerialize() method that returns the properties you want in the JSON result in a format accessible to json_encode (an object with public properties or an array).

infinite loop on a redirect route

i have a problem i dont know how to stop it. i have an infinite loop on a redirect route, i tried to redirect a user if he has not accept some condition
this is my code :
/**
* AcceptanceListener constructor.
* #param $router
* #param TokenStorageInterface $tokenStorage
* #param UserManager $userManager
* #param AuthorizationChecker $checker
*/
public function __construct(
$router,
TokenStorageInterface $tokenStorage,
UserManager $userManager,
AuthorizationChecker $checker
) {
$this->router = $router;
$this->tokenStorage = $tokenStorage;
$this->userManager = $userManager;
$this->role = $checker;
}
/**
* To checked all the routes
*
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$user = $this->tokenStorage->getToken()->getUser();
if ($this->role->isGranted('ROLE_USER')
&& !$this->userManager->hasAccepted($user)
) {
$this->redirect = $this->router->generate('accept_');
$event->setResponse(new RedirectResponse($this->redirect));
}
}
someone can help me please?
Check if the route is not the same, then redirect
public function onKernelRequest(GetResponseEvent $event)
{
$route_name = $event->getRequest()->get('_route');
if ($route_name != 'accept_') {
$user = $this->tokenStorage->getToken()->getUser();
if ($this->role->isGranted('ROLE_USER')
&& !$this->userManager->hasAccepted($user)
) {
$this->redirect = $this->router->generate('accept_');
$event->setResponse(new RedirectResponse($this->redirect));
}
}
}

How to change value of field in FOSRestBundle/JMS Serializer?

I have the field "image" in entities. But depends of action I want to show not original image, but image's preview (which I make in LiipImagineBundle). The one solution which I can imagine:
public function cgetAction(Request $request)
{
$events = $this->container->get('gc.event_manager')->getEvents();
foreach ($events as &$event) {
$previewURL = $this->getPreview($event->getPhoto());
$event->setPhoto($previewURL);
}
$event = false;
return array(
'events' => $events,
);
}
But I don't like it, because if an entity has deep children's entities the code will be very confusing.
How to do it correctly?
Common problem when you want to return absolute url through API while using LiipImagine and FOSRest
Change EE\TYSBundle to your own, code taken from https://github.com/EE/TellYourStoryBundle/tree/develop
JMSSerializer Handler Service with injected Cache Manager to get correct prefix
ee_tys.serializer.filename_handler:
class: EE\TYSBundle\Handler\FilenameHandler
arguments:
- "#liip_imagine.cache.manager"
tags:
- { name: 'jms_serializer.handler', type: Filename, format: json}
Handler for custom Filename type
EE\TYSBundle\Handler\FilenameHandler.php
<?php
namespace EE\TYSBundle\Handler;
use EE\TYSBundle\Entity\Filename;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\VisitorInterface;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
class FilenameHandler implements SubscribingHandlerInterface
{
function __construct(CacheManager $manager)
{
$this->manager = $manager;
}
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'Filename',
'method' => 'serializeFilenameToJson',
),
);
}
public function serializeFilenameToJson(VisitorInterface $visitor, Filename $filename, array $type)
{
// `tile` is a name of Imagine filter you want to apply
return $this->manager->getBrowserPath($filename, 'tile', true);
}
}
EE\TYSBundle\Entity\Filename
<?php
namespace EE\TYSBundle\Entity;
/**
* Class Filename
* #package EE\TYSBundle\Entity
*/
class Filename {
/**
* #var string
*/
public $name;
/**
* #var string
*/
public $extension;
public function __construct($filename)
{
$parts = explode(".", $filename);
$this->setName($parts[0]);
$this->setExtension($parts[1]);
}
/**
* #param mixed $extension
* #return Media
*/
public function setExtension($extension)
{
$this->extension = $extension;
return $this;
}
/**
* #return mixed
*/
public function getExtension()
{
return $this->extension;
}
/**
* #param mixed $name
* #return Filename
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #return string filename
*/
public function __toString()
{
return join(".", array($this->name, $this->extension));
}
}
usage of custom Filename type
/**
* #var string|null
*
* #ORM\Column(name="background_filename", type="string", length=255, nullable=true)
*
* #Serializer\Expose
* #Serializer\Type("Filename")
* #Serializer\SerializedName("background_uri")
*/
private $backgroundFilename;
From now on you will get background_uri with absolute url to your preview Image
One possible solution is to define Handler.
Handlers allow you to change the serialization, or deserialization
process for a single type/format combination.
Something like
class ImageHandler
{
public function serialize(VisitorInterface $visitor, \FQCN\Image $image, array $type, Context $context)
{
// do some stuff
return ...;
}
}
And register it in services.yml
serializer.handler.image_handler:
class: FQCN\Handler\ImageHandler
arguments: []
tags:
- { name: "jms_serializer.handler", type: FQCN\AdvertImage, format: json, method: serialize }
If i understand your question well, you need change value for correct before serialisation, i think manual can help #Accessor
class User
{
private $id;
/** #Accessor(getter="getTrimmedName",setter="setName") */
private $name;
// ...
public function getTrimmedName()
{
return trim($this->name);
}
public function setName($name)
{
$this->name = $name;
}
}

Categories