Yii2. "With" in ManyToMany - php

I have 3 models: Image, Company and File. So if we look through Company model, we have:
/**
* #return \yii\db\ActiveQuery
*/
public function getImages()
{
return $this->hasMany('galleries\models\Image', ['id' => 'image_id'])
->viaTable('{{%companies_has_images}}', ['company_id' => 'id']);
}
public function extraFields()
{
return ['images'];
}
now an Image model:
/**
* #return \yii\db\ActiveQuery
*/
public function getFile()
{
return $this->hasOne('app\models\File', ['id' => 'file_id']);
}
public function extraFields()
{
return ['file'];
}
So here is the question, how can i get images with correct files in getImages() in the Company model?

You'll have to fetch the images first and then provide an extra getter function to return the files:
public function getImageFiles()
{
$files = [];
foreach ($this->images as $image)
$files[] = $image->file;
return $files;
}

Related

change parameter value of dependent method in phpunit

i wrote a sample test case for collection like class but weird thing about this is in my testAdd method that i add a item in CustomCollectionService and it changed my parameter too. how can this happend?
class CustomCollectionService
{
/**
* #var Collection $collection
*/
public $collection;
public function makeCollection($arr)
{
$this->collection = collect($arr);
}
/**
* #param Collection $collection
*/
public function setCollection(Collection $collection): void
{
$this->collection = $collection;
}
/**
* #return mixed
*/
public function getCollection()
{
return $this->collection;
}
public function add($item)
{
return $this->collection->add($item);
}
}
and this is my test:
class CustomCollectionTest extends TestCase
{
public $collectionService;
public $collection;
protected function setUp(): void
{
$this->collectionService = new CustomCollectionService();
}
public function testCollectionCreator()
{
$arr = ['sina','1',5];
$this->assertIsArray($arr);
return $arr;
}
/**
* #param $arr
* #depends testCollectionCreator
*/
public function testAction($arr)
{
$this->collectionService->makeCollection($arr);
$this->assertIsArray($this->collectionService->getCollection()->toArray());
return $this->collectionService->getCollection();
}
/**
* #depends testAction
*/
public function testAdd($col)
{
$actualCount = $col->count();
$this->collectionService->setCollection($col);
$manipulatedCollection = $this->collectionService->add(['xx']);
dump($actualCount); // 3
dump($col->count()); //4
$this->assertEquals($actualCount+1, $manipulatedCollection->count());
}
}
Because it is an object. So when you pass the $col object to the CollectionService and call the add method within the CollectionService, it is still the $col object from your test method that is being used.

Syntax folder repository for the function edit()

I have 2 foreign keys which are fk_author and fk_bookcase , I am trying to create my function edit() via a folder Repositorie but I am stuck on the syntax again.
Here is my code via the file BookRepository
public function edit($id)
{
$books = Book::find($id);
$authors = Author::all();
$bookcases = Bookcase::all();
return Book::find($id);
}
Then, in my Controller I have this...
public function edit($id)
{
$books = $this->books->edit($id);
return view('admin.books.edit', compact('books', 'authors', 'bookcases'));
}
Do you have an idea of the problem?
Regards
If you want to retrieve the book with the related 'author' and 'bookcase', you must have defined the relations in the models. For ex:
Book Model
public function author()
{
return $this->belongsTo(Author::class, 'fk_author'); // change fk_author for the key you are using
}
public function bookcase()
{
return $this->belongsTo(Bookcase::class, 'fk_bookcase');
}
Author Model
public function books()
{
return $this->hasMany(Book::class);
}
Bookcase Model
public function books()
{
return $this->hasMany(Book::class);
}
And you doesn't need and edit() function in your repository, just a detail() (or the name what you want) which retrive the Book Object with the relations.
BookRepository
public function detail($id)
{
return Book::with([
'author',
'bookcase',
])
->find($id);
}
Then, in the Controller, yes, you have an edit function which get the detail from the repository and return the object to the edit view.
/**
* #var BookRepository
*/
private $books;
public function __construct(BookRepository $books)
{
$this->books = $books;
}
public function edit($id)
{
$book = $this->books->detail($id);
return view('admin.books.edit', compact('book'));
}
If in any case you want to also return all the authors and bookcases, I think it is better to make a repository for each one, so you can also use them from other Controllers or Classes.
AuthorRepository
public function getAll()
{
return Author::all();
}
BookcaseRepository
public function getAll()
{
return Bookcase::all();
}
Then, in the Controller
/**
* #var BookRepository
*/
private $books;
/**
* #var AuthorRepository
*/
private $authors;
/**
* #var BookcaseRepository
*/
private $bookcases;
public function __construct(BookRepository $books, AuthorRepository $authors, BookcaseRepository $bookcases)
{
$this->books = $books;
$this->authors = $authors;
$this->bookscases = $bookcases;
}
public function edit($id)
{
$book = $this->books->detail($id);
$authors = $this->authors->getAll();
$bookcases = $this->bookcases->getAll();
return view('admin.books.edit', compact('book', 'authors', 'bookcases'));
}

Doctrine2 / transfer Collection element to another Collection

Here are 3 entities :
Entity A
class EntityA
{
/**
* #ORM\OneToMany(targetEntity="EntityB", mappedBy="entityA", cascade={"all"}, orphanRemoval=true)
*/
protected $entitiesB;
public function __construct()
{
$this->entitiesB = new ArrayCollection();
}
public function getEntitiesB()
{
return $this->entitiesB;
}
public function setEntitiesB($entitiesB)
{
$this->entitiesB = new ArrayCollection();
return $this->addEntitiesB($entitiesB);
}
public function addEntityB(EntityB $entityB)
{
if (!$this->entitiesB->contains($entityB))
{
$this->entitiesB->add($entityB);
$entityB->setEntityA($this);
}
return $this;
}
public function addEntitiesB($entitiesB)
{
foreach ($entitiesB as $entityB)
{
$this->addEntityB($entityB);
}
return $this;
}
public function removeEntityB(EntityB $entityB)
{
if ($this->entitiesB->contains($entityB))
{
$this->entitiesB->removeElement($entityB);
}
return $this;
}
public function removeEntitiesB($entitiesB)
{
foreach ($entitiesB as $entityB)
{
$this->removeEntityB($entityB);
}
return $this;
}
}
Entity B
class EntityB
{
/**
*
* #ORM\OneToMany(targetEntity="EntityC", mappedBy="entityB", cascade={"all"}, orphanRemoval=true)
*/
protected $entitiesC;
/**
* #ORM\ManyToOne(targetEntity="EntityA", inversedBy="entitiesB")
* #ORM\JoinColumn(name="entity_a_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $entityA;
public function __construct()
{
$this->entitiesC = new ArrayCollection();
}
public function getEntityA()
{
return $this->entityA;
}
public function setEntityA(EntityA $entityA)
{
$this->entityA = $entityA;
$entityA->addEntityB($this);
return $this;
}
public function getEntitiesC()
{
return $this->entitiesC;
}
public function setEntitiesC($entitiesC)
{
$this->entitiesC = new ArrayCollection();
return $this->addEntitiesC($entitiesC);
}
public function addEntityC(EntityC $entityC)
{
if (!$this->entitiesC->contains($entityC))
{
$this->entitiesC->add($entityC);
$entityC->setEntityB($this);
}
return $this;
}
public function addEntitiesC($entitiesC)
{
foreach ($entitiesC as $entityC)
{
$this->addEntityC($entityC);
}
return $this;
}
public function removeEntityC(EntityC $entityC)
{
if ($this->entitiesC->contains($entityC))
{
$this->entitiesC->removeElement($entityC);
}
return $this;
}
public function removeEntitiesC($entitiesC)
{
foreach ($entitiesC as $entityC)
{
$this->removeEntityC($entityC);
}
return $this;
}
}
Entity C
class EntityC
{
/**
* #ORM\ManyToOne(targetEntity="EntityB", inversedBy="entitiesC")
* #ORM\JoinColumn(name="entity_b_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $entityB;
public function getEntityB()
{
return $this->entityB;
}
public function setEntityB(EntityB $entityB)
{
$this->entityB = $entityB;
$entityB->addEntityC($this);
return $this;
}
}
So, now, assume that we have this data :
EntityA [
EntitiesB [
EntityB1 [
EntitiesC [
EntityC1
EntityC2
EntityC3
]
]
EntityB2 [
EntitiesC [
EntityC4
]
]
]
]
That I want is to transfer EntityC4 to the EntityB1[EntitieC] collection.
To achieve that, the process would be :
- EntityB2.EntitiesC::removeEntityC(EntityC4)
- EntityB1.EntitiesC::addEntity(EntityC4)
But it will not do the trick ... EntityC4 is removed and not transferred !
So, it works when orphanRemoval=false on EntityB.EntitiesC, but I want to keep this Doctrine flag.
Is it another way to achieve that properly ?
Thanks for your ideas.
If you read the Doctrine documentation chapter 8.7. Orphan Removal you can see the following:
When using the orphanRemoval=true option Doctrine makes the assumption that the entities are privately owned and will NOT be reused by other entities. If you neglect this assumption your entities will get deleted by Doctrine even if you assigned the orphaned entity to another one.
Seems to me that this this is exactly the mistake you are making in this case.
You simply cannot use orphanRemoval in this case.

Yii 2 check if function exist in yii\base\Model

I am trying to solve problem of cheeking function in standart gii
I need to check if function exist in yii\base\Model
and if it exist, add prefix to this function
For example if you generate model with yii2\gii
you will have somthing like this
/**
* #return \yii\db\ActiveQuery
*/
public function getErrors()
{
return $this->hasMany(Error::className(), ['groupId' => 'id']);
}
I need to change function name when it generate to
/**
* #return \yii\db\ActiveQuery
*/
public function funky_key_getErrors()
{
return $this->hasMany(Error::className(), ['groupId' => 'id']);
}
I extend basics gii and rewrite function but it doesn't help
My code from generators\model\Generator, i think i need to check $relations in this function
protected function generateRelations()
{
\before basik yii code\
$relations = self::checkExistClass($relations);
return $relations;
}
private static function checkExistClass($relations)
{
foreach ($relations as $name => $relation) {
foreach ($relation as $functionName => $functionValue) {
$functionNameGet = 'get' . $functionName;
$directory = new Model;
if (method_exists($directory, $functionNameGet)) {
$relation['funky_key_' . $functionName] = $functionValue;
unset($relation[$functionName]);
}
}
}
return $relations;
}

Set a list of images to an entity with VichUploaderBundle

I have an entity "Comment", and a "Comment" could have one or more images associated.
How do i achieve that.
Now i have this(for just one image):
/**
* #Assert\File(
* maxSize="1M",
* mimeTypes={"image/png", "image/jpeg"}
* )
* #Vich\UploadableField(mapping="comment_mapping", fileNameProperty="imageName")
*
* #var File $image
*/
protected $image;
Thanks in advance
You have to create a ManyToOne relationship between your Comment and Image entities.
Read more on associations with doctrine 2 here.
Comment
/**
* #ORM\ManyToOne(targetEntity="Image", inversedBy="comment")
*/
protected $images;
public function __construct()
{
$this->images = new ArrayCollection();
}
public function getImages()
{
return $this->images;
}
public function addImage(ImageInterface $image)
{
if (!$this->images->contains($image)) {
$this->images->add($image);
}
return $this;
}
public function removeImage(ImageInterface $image)
{
$this->images->remove($image);
return $this;
}
public function setImages(Collection $images)
{
$this->images = $images;
}
// ...
Image
protected $comment;
public function getComment()
{
return $this->comment;
}
public function setComment(CommentInterface $comment)
{
$this->comment = $comment;
return $this;
}
// ...
Then add a collection form field to your CommentFormType with "type" of ImageFormType ( to be created ).

Categories