I used DoctrineFixturesBundle to fill my db for 50000 records.I want to have many employee to one boss. But now when I filled my db I have in boss_id only null values
My load function:
public function load(ObjectManager $manager)
{
for ($i = 0; $i <= 50000; $i++) {
$employee = new Employee();
if($i == 0) {
$employee->setPosition('founder');
}
$employee->setFullName('new employee'.$i);
$employee->setSalary(50000 - $i);
$employee->setStartDate(new \DateTime());
if($i > 0) {
$employee->setBoss($manager->find(Employee::class,1));
$employee->setPosition('boss');
} elseif ($i >= 1000) {
$id = rand(2,1000);
$employee->setBoss($manager->find(Employee::class,$id));
$employee->setPosition('top-manager');
} elseif ($i >= 5000) {
$id = rand (1000,4999);
$employee->setBoss($manager->find(Employee::class,$id));
$employee->setPosition('manager');
} elseif ($i >= 10000){
$id = rand(5000,9999);
$employee->setBoss($manager->find(Employee::class,$id));
$employee->setPosition('worker');
}
$manager->persist($employee);
}
$manager->flush();
}
My Entity Employee:
/**
* #var Employee
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Employee")
*/
private $boss;
public function setBoss(\AppBundle\Entity\Employee $boss = null)
{
$this->boss = $boss;
return $this;
}
how can I do this trick? What I did wrong?
It is because your object is not created when you use it
use the addReferenceas explained in the doc
Related
My problem comes from the fact that i have a relation in my entities that depends on each other. My entity Link has a relation with an entity User. When I try to load the User using UserManagerInterface it returns NULL.
Here is My AppFixtures class:
<?php
namespace App\DataFixtures;
use App\Component\Localization;
use App\Entity\Link;
use App\Entity\LinkClick;
use App\Entity\LinkView;
use App\Entity\Visitor;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker;
use FOS\UserBundle\Model\UserManagerInterface;
use PUGX\Shortid\Shortid;
class AppFixtures extends Fixture
{
private $userManager;
private $localisation;
public function __construct(UserManagerInterface $userManager, Localization $localization)
{
$this->userManager = $userManager;
$faker = Faker\Factory::create();
$localization->setParameters($localization::GEO_PLUGIN, $faker->ipv4);
$localization->localize();
$this->localisation = $localization->getResult();
}
public function load(ObjectManager $manager): void
{
$faker = Faker\Factory::create();
$user = $this->userManager->createUser();
$user->setUsername('admin');
$user->setEmail('admin#kss.tk');
$user->setPlainPassword('admin');
$user->setEnabled(true);
$user->setRoles(['ROLE_ADMIN']);
$this->userManager->updateUser($user);
$links = [];
$visitors = [];
for ($i = 0; $i < 100; ++$i) {
$link = new Link();
$link->setUrl($faker->url);
$link->setSlug(Shortid::generate(12, 'abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01', true));
$link->setCreatedAt($faker->dateTime);
$link->setUser($user);
$links[] = $link;
$manager->persist($link);
}
for ($i = 0; $i < 10000; ++$i) {
$visitor = new Visitor();
$visitor->setIp($this->localisation->geoplugin_request);
$visitor->setCountryCode($this->localisation->geoplugin_countryCode);
$visitor->setCountryName($this->localisation->geoplugin_countryName);
$visitors[] = $visitor;
$manager->persist($visitor);
}
for ($i = 0; $i < 10000; ++$i) {
$linkView = new LinkView();
$linkView->setDate($faker->dateTimeBetween('-1 years', 'now'));
$linkView->setVisitor($visitors[rand(0, 999)]);
$linkView->setLink($links[rand(0, 99)]);
$manager->persist($linkView);
}
for ($i = 0; $i < 10000; ++$i) {
$linkClick = new LinkClick();
$linkClick->setDate($faker->dateTimeBetween('-1 years', 'now'));
$linkClick->setVisitor($visitors[rand(0, 999)]);
$linkClick->setLink($links[rand(0, 99)]);
$manager->persist($linkClick);
}
$manager->flush();
}
}
I dont want to flush the database everytime, i want to use and existing user, so i can append fixtures. Right now it throws an error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'admin' for key 'UNIQ_1483A5E992FC23A8'
I am trying to load fixtures into my database. I have a post entity and a category entity, and a category can have many posts.
In my fixtures file I would like to create some categories and then assign a random category to each post, but I am not sure how to do this.
How can I get a reference to a random category?
<?php
namespace App\DataFixtures;
use App\Entity\Category;
use App\Entity\User;
use App\Entity\Post;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use Faker\Factory;
use Faker\Generator;
class AppFixtures extends Fixture
{
private $faker;
public function load(ObjectManager $manager)
{
$this->faker = Factory::create();
$this->addUsers($manager);
$this->addCategories($manager);
$this->addPosts($manager);
$manager->flush();
}
private function addUsers(EntityManager $em)
{
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$firstname = $this->faker->firstName;
$lastname = $this->faker->lastName;
$user->setFirstName($firstname);
$user->setLastName($lastname);
$user->setEmail($firstname.'.'.$lastname.'#gmail.com');
$user->setRoles(['ROLE_USER']);
$em->persist($user);
$this->addPosts($user);
}
}
private function addCategories(EntityManager $em)
{
$categoryHome = new Category();
$categoryHome->setName('Home');
$em->persist($categoryHome);
$categoryWork = new Category();
$categoryWork->setName('Work');
$em->persist($categoryWork);
}
public function addPosts(EntityManager $em, User $user){
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
// How can I assign the category randomly?
$post->setCaterory(????)
$em->persist($post);
}
}
}
If you need to create random data (for example for presentation) I think better choice would be using AliceBundle https://github.com/hautelook/AliceBundle which can do this out of box via relations. Plus it will be same on every load of fixtures.
But to answer your question. You make list of categories you created and then just choose one randomly.
<?php
namespace App\DataFixtures;
use App\Entity\Category;
use App\Entity\User;
use App\Entity\Post;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use Faker\Factory;
use Faker\Generator;
class AppFixtures extends Fixture
{
private $faker;
private $categories;
public function load(ObjectManager $manager)
{
$this->faker = Factory::create();
$this->addUsers($manager);
$this->addCategories($manager);
$this->addPosts($manager);
$manager->flush();
}
private function addUsers(EntityManager $em)
{
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$firstname = $this->faker->firstName;
$lastname = $this->faker->lastName;
$user->setFirstName($firstname);
$user->setLastName($lastname);
$user->setEmail($firstname.'.'.$lastname.'#gmail.com');
$user->setRoles(['ROLE_USER']);
$em->persist($user);
$this->addPosts($user);
}
}
private function addCategories(EntityManager $em)
{
$categoryHome = new Category();
$categoryHome->setName('Home');
$em->persist($categoryHome);
$this->categories[] = $categoryHome;
$categoryWork = new Category();
$categoryWork->setName('Work');
$em->persist($categoryWork);
$this->categories[] = $categoryWork;
}
public function addPosts(EntityManager $em, User $user){
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
$post->setCaterory($this->categories[rand(0, count($this->categories))]);
$em->persist($post);
}
}
}
In AddPost Function Add this
public function addPosts(EntityManager $em, User $user){
$categories = $manager->getRepository(Category::class)->findAll();
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
$category = $categories[array_rand($categories)];
$post->setCategory($category)
$em->persist($post);
}
}
}
I know there are the ways to override module's js/css/tpl and other files, but I cant find a way to override, for example, the /js/validate.js file.
I've tried to place my own file to /themes/my-theme/js folder, but I cant see a result (validate.js is still loaded from /js/ folder).
Is there a way to do this?
There is no built-in functionality to do this.
But you can override the method addMedia of FrontController.
Create this file /override/classes/controller/FrontController.php:
<?php
class FrontController extends FrontControllerCore
{
/**
* Adds a media file(s) (CSS, JS) to page header
*
* #param string|array $media_uri Path to file, or an array of paths like: array(array(uri => media_type), ...)
* #param string|null $css_media_type CSS media type
* #param int|null $offset
* #param bool $remove If True, removes media files
* #param bool $check_path If true, checks if files exists
* #return true|void
*/
public function addMedia($media_uri, $css_media_type = null, $offset = null, $remove = false, $check_path = true)
{
if (!is_array($media_uri)) {
if ($css_media_type) {
$media_uri = array($media_uri => $css_media_type);
} else {
$media_uri = array($media_uri);
}
}
$list_uri = array();
foreach ($media_uri as $file => $media) {
if (!Validate::isAbsoluteUrl($media)) {
$different = 0;
$different_css = 0;
$type = 'css';
if (!$css_media_type) {
$type = 'js';
$file = $media;
}
if (strpos($file, __PS_BASE_URI__.'modules/') === 0) {
$override_path = str_replace(__PS_BASE_URI__.'modules/', _PS_ROOT_DIR_.'/themes/'._THEME_NAME_.'/'.$type.'/modules/', $file, $different);
if (strrpos($override_path, $type.'/'.basename($file)) !== false) {
$override_path_css = str_replace($type.'/'.basename($file), basename($file), $override_path, $different_css);
}
if ($different && #filemtime($override_path)) {
$file = str_replace(__PS_BASE_URI__.'modules/', __PS_BASE_URI__.'themes/'._THEME_NAME_.'/'.$type.'/modules/', $file, $different);
} elseif ($different_css && #filemtime($override_path_css)) {
$file = $override_path_css;
}
if ($css_media_type) {
$list_uri[$file] = $media;
} else {
$list_uri[] = $file;
}
// Here we add our test to override default js files
} elseif (strpos($file, _PS_JS_DIR_) === 0) {
$override_path = str_replace(_PS_JS_DIR_, _PS_ROOT_DIR_.'/themes/'._THEME_NAME_.'/js/default/', $file, $different);
if (strrpos($override_path, $type.'/'.basename($file)) !== false) {
$override_path_css = str_replace($type.'/'.basename($file), basename($file), $override_path, $different_css);
}
if ($different && #filemtime($override_path)) {
$file = str_replace(_PS_JS_DIR_, __PS_BASE_URI__.'themes/'._THEME_NAME_.'/js/default/', $file, $different);
} elseif ($different_css && #filemtime($override_path_css)) {
$file = $override_path_css;
}
if ($css_media_type) {
$list_uri[$file] = $media;
} else {
$list_uri[] = $file;
}
} else {
$list_uri[$file] = $media;
}
} else {
$list_uri[$file] = $media;
}
}
if ($remove) {
if ($css_media_type) {
return parent::removeCSS($list_uri, $css_media_type);
}
return parent::removeJS($list_uri);
}
if ($css_media_type) {
return parent::addCSS($list_uri, $css_media_type, $offset, $check_path);
}
return parent::addJS($list_uri, $check_path);
}
}
With this override, you can now create a file /themes/my_theme/js/default/validate.js to override the default /js/validate.js file.
PS: as with any override, you will have to delete the file /cache/class_index.php.
I'm building a multi-tenant application. In Symfony1 I would restrict access to data by accessing the user details and extending the createQuery function:
class PersonTable extends Doctrine_Table{
public function createQuery($alias = '')
{
$query = parent::createQuery($alias);
try {
$user = sfContext::getInstance()->getUser();
}catch(Exception $e){
if ($e->getMessage() == 'The "default" context does not exist.'){
return $query;
}else{
throw $e;
}
}
if ($user->hasGroup('Team1')){
//all good
}else if ($user->hasGroup('Team2')){
$user_id = $user->getGuardUser()->getStaff()->getId();
$alias = $query->getRootAlias();
$time = date('Y-m-d H:i:s',time());
$query->andWhere("$alias.type='type1' and pe.assigned_psw_id");
}
$query->orderBy('name asc');
return $query;
}
}
I know there are downsides to accessing the user object through sfContext in sf1, but this method seemed superior to others, as you can't "forget" to secure a controller against wrong user access.
How can I achieve the same in Symfony2?
I have solved this problem the following way.
Standardise how EntityRepository is fetched among controllers:
public function getUserRestrictedRepository($entity, $em = null )
{
$securityContext = $this->get( 'security.context' );
if (!$em){
$em = $this->getDoctrine()->getManager();
}
return $em
->getRepository( 'MyBundle:' . $entity )
->setSecurityContext( $securityContext );
}
Add a trait to provide queries with injected security query:
trait UserRestrictedEntityRepository {
private $securityContext;
/**
* #return mixed
*/
public function getSecurityContext()
{
return $this->securityContext;
}
/**
* #param mixed $securityContext
*/
public function setSecurityContext($securityContext)
{
$this->securityContext = $securityContext;
return $this;
}
/**
* #return mixed
*/
public function getUser()
{
return $this->getSecurityContext()->getToken()->getUser();
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
function secureQueryWithUser($alias, $qb)
{
$qb->where("1=0");
}
function appendOrderBy($qb, $orderBy)
{
$first = true;
foreach ($orderBy as $field => $dir) {
if (!$dir) $dir = 'asc';
if ($first) {
$qb->orderBy('c.' . $field, $dir);
$first = false;
}else{
$qb->addOrderBy('c.' . $field, $dir);
}
}
}
public function createUnrestrictedQueryBuilder($alias)
{
return parent::createQueryBuilder($alias);
}
/**
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
*
* #param string $alias
*
* #return QueryBuilder
*/
public function createQueryBuilder($alias, $indexBy=NULL)
{
if ($this->getUser()) {
$qb = $this->_em->createQueryBuilder()
->select($alias)
->from($this->_entityName, $alias);
if (isset($this->defaultOrder) && $this->defaultOrder){
$this->appendOrderBy($qb, $this->defaultOrder);
}
if ($this->getUser()->isSuperAdmin()){
return $qb;
}else{
return $this->secureQueryWithUser($alias, $qb);
}
}else{
throw new Exception('Run setUser() before querying ' . $this->getName() .' model.');
}
}
/**
* Finds all entities in the repository.
*
* #return array The entities.
*/
public function findAll()
{
return $this->findBy(array());
}
/**
* Finds entities by a set of criteria.
*
* #param array $criteria
* #param array|null $orderBy
* #param int|null $limit
* #param int|null $offset
*
* #return array The objects.
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
$qb = $this->createQueryBuilder('c');
foreach ($criteria as $fkey => $fval){
$qb->andWhere($fkey.' = :'.$fval);
}
if ($limit){
$qb->setMaxResults($limit);
}
if ($offset){
$qb->setFirstResult($offset);
}
$query = $qb->getQuery();
return $query->getResult();
}
}
Implement query additions based on user access in the EnityRepository
class FarmerRepository extends EntityRepository
{
use UserRestrictedEntityRepository;
private $name = 'Farmer';
private $defaultOrder = array('name' => 'asc');
function secureQueryWithUser($alias, $qb)
{
if ($this->getSecurityContext()->isGranted( 'ROLE_CLINIC_ADMIN' )) {
return $qb
->innerJoin("$alias.vet", 'v')
->innerJoin("v.clinic", "cl")
->innerJoin("cl.VetsOfClinic", "vc")
->andWhere('vc.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else if ($this->getSecurityContext()->isGranted( 'ROLE_VET' )){
return $qb
->innerJoin("$alias.vet", 'v')
->andWhere('v.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else{
return $qb
->where("$alias.user_id= :userid")
->setParameter('userid', $this->getUser()->getId());
}
}
}
Working with Propel ORM 1.5, I'm missing a method to merge two PropelCollections.
A short proposal may be :
public function mergeCollection($collection){
foreach($collection as $i => $item){
if( ! $this->contains($item)){
// append item
$this->append($item);
}
}
}
So I'm new to Propel I would like to ask you, if there are better ways to do it ?
Or is this functionality already included in Propel, but i didn't yet discovered it ?
It seems to have been discuted twice in the mailing list, but I can't find the ticket.
At least, you can try this code and/or open a ticket on Github.
/**
* Add a collection of elements, preventing duplicates
*
* #param array $collection The collection
*
* #return int the number of new element in the collection
*/
public function addCollection($collection)
{
$i = 0;
foreach($collection as $ref) {
if ($this->add($ref)) {
$i = $i + 1;
}
}
return $i;
}
/**
* Add a an element to the collection, preventing duplicates
*
* #param $element The element
*
* #return bool if the element was added or not
*/
public function add($element)
{
if ($element != NULL) {
if ($this->isEmpty()) {
$this->append($element);
return true;
} else if (!$this->contains($element)) {
set_error_handler("error_2_exception");
try {
if (!method_exists($element, 'getPrimaryKey')) {
restore_error_handler();
$this->append($element);
return true;
}
if ($this->get($element->getPrimaryKey()) != null) {
restore_error_handler();
return false;
} else {
$this->append($element);
restore_error_handler();
return true;
}
} catch (Exception $x) {
//il semble que l'element ne soit pas dans la collection
restore_error_handler(); //restore the old handler
$this->append($element);
return true;
}
restore_error_handler(); //restore the old handler
}
}
return false;
}
}
function error_2_exception($errno, $errstr, $errfile, $errline,$context) {
throw new Exception('',$errno);
return true;
}