I'm developing a crowfunding website and I have two entities: User and Campaign
One user can be two things: team and funder
for now I have:
class Campaign
{
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Many Groups have Many Users.
* #ORM\ManyToMany(targetEntity="User", mappedBy="teams")
*/
private $team;
/**
* #param Team $team
*/
public function setTeam(User $team)
{
$this->team[] = $teams;
}
public function getTeam()
{
return $this->team;
}
}
The problem is that the teams are not saved in the DB (all the other info on the form are saved normaly). Would you help me understand what I did wrong please ?
Thank you
Are you sure about your method setTeam ? You give $team in parameter but in your assignation of the array $this->team you only use $teams with an s and no the $team which is in param.
/**
* #param Team $team
*/
public function setTeam(User $team)
{
$this->team[] = $teams;
}
Related
i have created the project with user roles and permissions
Here is my Tables and Model
users--list of the application users --Model Name [User],
roles--list of the roles available inside the application --Model Name [Role],
permissions--list of the Permisisons available inside the application --Model Name [Permisions],
Here is my relationship tables
role_user Which hold the relationship between the roles table and users table
permission_role Which hold the relationship between the permissions table and roles table
permission_user Which hold the relationship between the permissions table and users table
My Relationship Code inside Model
User.php Model
/**
* Many-to-Many relations with Role.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
/**
* Many-to-Many relations with Permission.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
public function hasPermission($permission)
{
return $this->hasPermissionThroughRole($permission) || (bool) $this->permissions->where('name',$permission->name)->count();
}
public function hasPermissionThroughRole($permission)
{
foreach($permission->roles as $role)
{
if($this->roles->contains($role))
{
return true;
}
}
return false;
}
public function hasRoles($roles)
{
$roles = is_array($roles) ? $roles : func_get_args();
foreach ($roles as $role)
{
if ($this->hasRole($role))
{
return true;
}
}
return false;
}
/**
* Returns if the given user has an specific role.
*
* #param string $role
*
* #return bool
*/
public function hasRole($role)
{
return $this->roles
->where('name', $role)
->first() != null;
}
Role.php Model
/**
* Many-to-Many relations with Permissions.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
/**
* Many-to-Many relations with Users.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
{
return $this->belongsToMany(User::class);
}
Permission.php Model
/**
* Belongs-to-Many relations with Role.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
/**
* Belongs-to-Many relations with User.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
{
return $this->belongsToMany(User::class);
}
/**
* Belongs-to-Many relations with Modules.
*
* #return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
And then finally i have create the ServiceProvider named as
PermissionServiceProvider
and Inside the boot method of the serviceprovider i have added the code
public function boot()
{
if (Schema::hasTable('permissions'))
{
Permission::get()->map(function ($permission)
{
Gate::define($permission->name, function ($user) use ($permission)
{
return $user->hasPermission($permission);
});
});
}
Blade::directive('role', function ($role)
{
return "<?php if(Auth::user()->hasRole({$role})): ?>";
});
Blade::directive('endrole', function ($role)
{
return "<?php endif; ?>";
});
}
Every functions and relationship is working fine but the Queries are running every time i hit the refresh button
Is there any way to cache all the permisisons and roles to logged in user
Edited
As per Some Suggestions i have tried laravel cache package
It is not working for me
You are looking for laravel model caching pakage https://github.com/GeneaLabs/laravel-model-caching.
I recommend to install package with redis. Also it is very useful when working with queues.
Works as expected. Screenshots from my project.
Before:
After:
For the past couple of days I have been trying to create a bidirectionnal ManyToOne-OneToMany relationship in Symfony 3.4
I have two entities. One is Contribution and the other is Source. A Contribution can have several sources. So the relationship should be
Contribution – ManyToOne – Source – OneToMany – Contribution
But I keep getting the following error during $em→flush(); in my controller:
Type error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in /var/www/html/Edebate/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 605
I do not have any set method related to the Array Collection in my Entity Contribution as I could see in other posts here:
Type error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given
Symfony-Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given
And the annotations are ok as mentionned here:
Doctrine OneToMany relationship error
Any help would be appreciate ! :)
Here is my Entity Contribution
use Doctrine\Common\Collections\ArrayCollection;
//annotations
abstract class Contribution
{
/**
* #ORM\OneToMany(targetEntity="Shaker\DebateBundle\Entity\Source", mappedBy="parent")
*/
protected $sources;
//Other attributes and methods
public function __construct() {
$this->sources = new ArrayCollection();
}
/**
* Add source
*
* #param \Shaker\DebateBundle\Entity\Source $source
*
* #return Contribution
*/
public function addSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources[] = $source;
return $this;
}
/**
* Remove source
*
* #param \Shaker\DebateBundle\Entity\Source $source
*/
public function removeSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources->removeElement($source);
}
/**
* Get sources
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSources()
{
return $this->sources;
}
}
And this is in my Entity Source:
/**
* #ORM\ManyToOne(targetEntity="Shaker\DebateBundle\Entity\Contribution", inversedBy="sources")
*/
protected $parent;
/**
* Set parent
*
* #param \Shaker\DebateBundle\Entity\Contribution $parent
*
* #return Contribution
*/
public function setParent(\Shaker\DebateBundle\Entity\Contribution $parent = null)
{
$this->parent = $parent;
$parent->addSource($this);
return $this;
}
/**
* Get parent
*
* #return \Shaker\JRQBundle\Entity\Contribution
*/
public function getParent()
{
return $this->parent;
}
And in my Controller, the problem arises with flush:
$formsourcebook->handleRequest($request);
$contributionid=$formsourcebook->get('ContributionId')->getData();
if ($formsourcebook->isValid()) {
$topicargtarget=$this->getContribution($contributionid);
$sourcebook->setUser($user);
$sourcebook->setContribution($topicargtarget);
$em->persist($sourcebook);
$em->flush();
}
I don't know your question very well. However, did you try with this syntax in the Source entity?
private $parent;
// ...
public function __construct() {
$this->parent = new ArrayCollection();
// or new \Doctrine\Common\Collections\ArrayCollection();
}
I think you're forgetting the constructor in the class.
I think you "switched" some logic when working with collections. Here's how I think your "add" method should look like:
public function addSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources[] = $source;
$source->setParent($this);
return $this;
}
And in the other entity:
public function setParent(\Shaker\DebateBundle\Entity\Contribution $parent = null)
{
$this->parent = $parent;
return $this;
}
There are missing variables in your controller snippet, together with the form fields definitions, so you shouldn't work that much after submitting the form. Try to directly map as many fields as you can (even via autoguessing), and even if it looks ugly, but works, but then you can beautify later. Just my two cents with several months of delay.
I have a doubt about code organization using symfony3 and doctrine: I'll try to explain as clear as I can. Let's say I have a FootballClub entity:
class FootballClub
{
// other code
private $memberships;
public function addMembership(Membership $membership) : FootballClub
{
$this->memberships[] = $membership;
return $this;
}
public function removeMembership(Membership $membership) : bool
{
return $this->memberships->removeElement($membership);
}
}
The entity is in a many-to-one relationship with another entity, Membership, which represents the contract a player has with the club. Let's say each club
has only a limited number of membership it can acquire, number that is represented as a setting, for example, as a property in a Setting entity.
The question is: how should I reference that setting when removing a membership from the club and check that is respected? Entities should not have any dependency, so what would be the correct way to implement this? A service? can you provide an example? Thank you for your time.
You could create a Settings entity, linked in OneToOne relation with FootballCluc entity.
Define Settings like this and instanciate it in the FootballClub's constructor
Settings entity
/** #Entity */
class Settings
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $maxMembership;
// Other configurable properties ...
__constructor($maxMembership = 50)
{
$this->maxMembership = $maxMembership;
}
public function getMaxMembership()
{
return $this->maxMembership;
}
public function setMaxMembership($maxMembership)
{
$this->maxMembership = $maxMembership;
}
}
Football Entity
class FootballClub
{
/**
* One FootballClub has One Settings.
* #OneToOne(targetEntity="Settings")
* #JoinColumn(name="settings_id", referencedColumnName="id")
*/
private $settings;
// other code
private $memberships;
__constructor(Settings $settings = null)
{
if (null === $settings) {
$settings = new Settings();
}
$this->settings = $settings;
}
public function addMembership(Membership $membership) : FootballClub
{
if ($this->settings->getMaxMembership() <= count($this->memberships)) {
// throw new Exception("Max number of membership reached"); Strict mode
// return false // soft mode
}
$this->memberships-> = $membership;
return $this;
}
public function removeMembership(Membership $membership) : bool
{
return $this->memberships->removeElement($membership);
}
}
I have 2 entities Submission and Documents. 1 Submission can have Multiple documents.
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Document", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $document;
/**
* #return mixed
*/
public function getDocument()
{
return $this->document->toArray();
}
public function setDocument(Document $document)
{
$this->document[] = $document;
return $this;
}
Document Entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="document")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id",onDelete="cascade", nullable=true)
*/
protected $submission;
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
After receiving files dropzonejs - I'm saving them into Document object, and then, i'm try to save this object into Submission, and persist.
$document = new Document();
$em = $this->getDoctrine()->getManager();
$media = $request->files->get('file');
foreach($media as $req){
$document->setFile($req);
$document->setPath($req->getPathName());
$document->setName($req->getClientOriginalName());
$em->persist($document);
}
$submission->setSubmissionStatus(true);
foreach($document as $item){
$submission->setDocument($item);
}
$submission->setUser($user);
$em = $this->getDoctrine()->getManager();
$em->persist($submission);
$em->flush();
Problem is that all the time, i'm receiving error that submission_title is not set, but that's not true, because i have set this field before. I haven't got idea, what is wrong.
I think you'll get some mileage out of following the tutorial over at http://symfony.com/doc/current/doctrine/associations.html, if you haven't already.
I can see that your getters / setters aren't optimal for associating more than one Document with your Submission.
As they write in the Symfony docs, where they want to associate one category with many products, they have the following code:
// src/AppBundle/Entity/Category.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Category
{
// ...
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
}
From the docs:
The code in the constructor is important. Rather than being
instantiated as a traditional array, the $products property must be of
a type that implements Doctrine's Collection interface. In this case,
an ArrayCollection object is used. This object looks and acts almost
exactly like an array, but has some added flexibility. If this makes
you uncomfortable, don't worry. Just imagine that it's an array and
you'll be in good shape.
So, you'll want to be sure the constructor for your Document entity has something like $this->submissions = new ArrayCollection();. I've changed the property to a plural name, because I think it's more semantically correct. But you can keep your $submission property name, if you like.
Next is to add a addSubmission, removeSubmission, and a getSubmissions method.
Then, your class might end up looking like this:
<?php
// src/AppBundle/Entity/Submission.php
namespace AppBundle\Entity
use Doctrine\Common\Collections\ArrayCollection;
class Submission
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Document", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*
* #var ArrayCollection()
*/
protected $documents;
...
/**
* Instantiates the Submission Entity
*
* #return void
*/
public function __construct()
{
$this->documents = new ArrayCollection();
}
/**
* Returns all documents on the Submission
*
* #return mixed
*/
public function getDocuments()
{
return $this->documents;
}
/**
* Add document to this Submission
*
* #param Document $document The object to add to the $documents collection.
*
* #return Submission
*/
public function setDocument(Document $document)
{
$this->documents[] = $document;
return $this;
}
/**
* Remove a document from this Submission
*
* #param Document $document The object to remove from the $documents collection.
*
* #return Submission
*/
public function removeDocument(Document $document)
{
$this->documents->removeElement($document);
return $this;
}
}
File based translations don't work for me because clients need to change the texts.
So I am thinking about implementing this interface to fetch data from the database and cache the results in an APC cache.
Is this a good solution?
This could be what you are looking for:
Use a database as a translation provider in Symfony 2
Introduction
This article explain how to use a database as translation storage in Symfony 2. Using a database to provide translations is quite easy to do in Symfony 2, but unfortunately it’s actually not explained in Symfony 2 website.
Creating language entities
At first, we have to create database entities for language management. In my case, I’ve created three entities : the Language entity contain every available languages (like french, english, german).
The second entity is named LanguageToken. It represent every available language tokens. The token entity represent the source tag of the xliff files. Every translatable text available is a token. For example, I use home_page as a token and it’s translated as Page principale in french and as Home page in english.
The last entity is the LanguageTranslation entity : it contain the translation of a token in a specific language. In the example below, the Page principale is a LanguageTranslation entity for the language french and the token home_page.
It’s quite inefficient, but the translations are cached in a file by Symfony 2, finally it’s used only one time at Symfony 2 first execution (except if you delete Symfony 2’s cache files).
The code of the Language entity is visible here :
/**
* #ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageRepository")
*/
class Language {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/** #ORM\column(type="string", length=200) */
private $locale;
/** #ORM\column(type="string", length=200) */
private $name;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getLocale() {
return $this->locale;
}
public function setLocale($locale) {
$this->locale = $locale;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
}
The code of the LanguageToken entity is visible here :
/**
* #ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageTokenRepository")
*/
class LanguageToken {
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/** #ORM\column(type="string", length=200, unique=true) */
private $token;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getToken() {
return $this->token;
}
public function setToken($token) {
$this->token = $token;
}
}
And the LanguageTranslation entity’s code is visible here :
/**
* #ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageTranslationRepository")
*/
class LanguageTranslation {
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/** #ORM\column(type="string", length=200) */
private $catalogue;
/** #ORM\column(type="text") */
private $translation;
/**
* #ORM\ManyToOne(targetEntity="YourApp\YourBundle\Entity\Language", fetch="EAGER")
*/
private $language;
/**
* #ORM\ManyToOne(targetEntity="YourApp\YourBundle\Entity\LanguageToken", fetch="EAGER")
*/
private $languageToken;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getCatalogue() {
return $this->catalogue;
}
public function setCatalogue($catalogue) {
$this->catalogue = $catalogue;
}
public function getTranslation() {
return $this->translation;
}
public function setTranslation($translation) {
$this->translation = $translation;
}
public function getLanguage() {
return $this->language;
}
public function setLanguage($language) {
$this->language = $language;
}
public function getLanguageToken() {
return $this->languageToken;
}
public function setLanguageToken($languageToken) {
$this->languageToken = $languageToken;
}
}
Implementing a LoaderInterface
The second step is to create a class implementing the Symfony\Component\Translation\Loader\LoaderInterface. The corresponding class is shown here :
class DBLoader implements LoaderInterface{
private $transaltionRepository;
private $languageRepository;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager){
$this->transaltionRepository = $entityManager->getRepository("AppCommonBundle:LanguageTranslation");
$this->languageRepository = $entityManager->getRepository("AppCommonBundle:Language");
}
function load($resource, $locale, $domain = 'messages'){
//Load on the db for the specified local
$language = $this->languageRepository->getLanguage($locale);
$translations = $this->transaltionRepository->getTranslations($language, $domain);
$catalogue = new MessageCatalogue($locale);
/**#var $translation Frtrains\CommonbBundle\Entity\LanguageTranslation */
foreach($translations as $translation){
$catalogue->set($translation->getLanguageToken()->getToken(), $translation->getTranslation(), $domain);
}
return $catalogue;
}
}
The DBLoader class need to have every translations from the LanguageTranslationRepository (the translationRepository member). The getTranslations($language, $domain) method of the translationRepository object is visible here :
class LanguageTranslationRepository extends EntityRepository {
/**
* Return all translations for specified token
* #param type $token
* #param type $domain
*/
public function getTranslations($language, $catalogue = "messages"){
$query = $this->getEntityManager()->createQuery("SELECT t FROM AppCommonBundle:LanguageTranslation t WHERE t.language = :language AND t.catalogue = :catalogue");
$query->setParameter("language", $language);
$query->setParameter("catalogue", $catalogue);
return $query->getResult();
}
...
}
The DBLoader class will be created by Symfony as a service, receiving an EntityManager as constructor argument. All arguments of the load method let you customize the way the translation loader interface work.
Create a Symfony service with DBLoader
The third step is to create a service using the previously created class. The code to add to the config.yml file is here :
services:
translation.loader.db:
class: MyApp\CommonBundle\Services\DBLoader
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: translation.loader, alias: db}
The transation.loader tag indicate to Symfony to use this translation loader for the db alias.
Create fake translation files
The last step is to create an app/Resources/translations/messages.xx.db file for every translation (with xx = en, fr, de, …).
I didn’t found the way to notify Symfony to use DBLoader as default translation loader. The only quick hack I’ve found is to create a app/Resources/translations/messages.en.db file. The db extension correspond to the db alias used in the service declaration. A corresponding file is created for every language available on the website, like messages.fr.db for french or messages.de.db for german.
When Symfony find the messages.xx.db file he load the translation.loader.db to manage this unknown extension and then the DBLoader use database content to provide translation.
I’ve also didn’t found the way to clean properly the translations cache on database modification (the cache have to be cleaned to force Symfony to recreate it). The code I actually use is visible here :
/**
* Remove language in every cache directories
*/
private function clearLanguageCache(){
$cacheDir = __DIR__ . "/../../../../app/cache";
$finder = new \Symfony\Component\Finder\Finder();
//TODO quick hack...
$finder->in(array($cacheDir . "/dev/translations", $cacheDir . "/prod/translations"))->files();
foreach($finder as $file){
unlink($file->getRealpath());
}
}
This solution isn’t the pretiest one (I will update this post if I find better solution) but it’s working ^^
Be Sociable, Share!
Take a look at the Translatable behavior extension for Doctrine 2. StofDoctrineExtensionsBundle integrates it with Symfony.
You may want to take a look into this Loader + Resource using PDO connection: https://gist.github.com/3315472
You then only need to make it cache aware, like adding a memcache, apc, .. in between.
If so, you can then disable the filecaching of the Translator itself.