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'));
}
Related
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.
I have these two classes with ManyToMany association:
One class:
/**
* #ORM\ManyToMany(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="tripEvents")
* #ORM\JoinTable(name="event_trip_registrators")
*/
private $tripRegistrators;
public function __construct()
{
$this->tripRegistrators = new ArrayCollection();
}
public function getTripRegistrators()
{
return $this->tripRegistrators;
}
public function setTripRegistrators($tripRegistrators)
{
$this->tripRegistrators = $tripRegistrators;
}
public function addTripRegistrator(User $tripRegistrator)
{
$this->tripRegistrators->add($tripRegistrator);
}
public function removeTripRegistrator($tripRegistrator)
{
$this->tripRegistrators->removeElement($tripRegistrator);
}
Second class:
/**
* #ORM\ManyToMany(targetEntity="Bundle\Entity\Event", mappedBy="tripRegistrators")
*/
protected $tripEvents;
public function __construct()
{
parent::__construct();
$this->tripEvents = new ArrayCollection();
}
public function getTripEvents()
{
return $this->tripEvents;
}
public function setTripEvents($tripEvents)
{
$this->tripEvents = $tripEvents;
}
If I call $event->getTripRegistrators() (first class), I only get an empty persistent collection.
Do you have any hint why this happens?
If I save items via SonataAdmin, everything works fine, the database table has correct data.
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;
}
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;
}
I am trying to implement a simple menu composite pattern.
These are the following classes i came up with.
MenuItem:
namespace MYNAME\MYBUNDLE\Entity;
use MYNAME\MYBUNDLE\Menu\MenuComponent;
class MenuItem implements MenuComponent
{
private $id;
private $name;
private $path;
private $parent;
private $visible;
private $createdOn;
private $templating;
private $attr;
private $children;
private $website;
private $position = 1;
public function __construct($name = null, $path = null, $attr = array(), $visible = true)
{
$this->name = $name;
$this->path = $path;
$this->visible = $visible;
$this->attr = $attr;
$this->createdOn = new \DateTime;
}
public function prePersist()
{
$this->createdOn = new \DateTime;
}
public function build()
{
$data['menu_item'] = $this;
$data['options'] = $this->attr;
if($this->hasChildren())
return $this->templating->render('MYBUNDLE:Menu:menu_dropdown.html.twig', $data);
if($this->isChild())
return $this->parent->getTemplating()->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
return $this->templating->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
}
public function __toString()
{
return $this->name;
}
public function setTemplating($templating)
{
$this->templating = $templating;
}
/**
* #return bool
*/
public function isChild()
{
return $this->hasParent();
}
/**
* #return bool
*/
public function hasParent()
{
return isset($this->parent);
}
/**
* #return bool
*/
public function hasChildren()
{
return count($this->children) > 0;
}
}
If left out the getters and setters to make it a bit shorter here.
As you can see this is the entity and it contains a build() function, however this function uses the render method which in my opinion shouldn't be in an entity.
MenuController
<?php
namespace MYNAME\MYBUNDLE\Controller;
use MYNAME\MYBUNDLE\Menu\Menu;
use MYNAME\MYBUNDLE\Entity\MenuItem;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class MenuController extends Controller
{
public function generateAction()
{
$menu = new Menu($this->get('templating'));
// load menu items
$items = $this->getDoctrine()->getRepository('MYBUNDLE:MenuItem')->findOrdered();
foreach($items as $item)
{
if(!$item->hasParent())
$menu->add($item);
}
return new Response($menu->build());
}
}
The MenuController gets called to render the menu:
{{ render(controller('MYBUNDLE:Menu:generate')) }}
I would also like this to be different since it doesn't look right. Perhaps it's better to create a twig function to render the menu?
MenuComponent:
namespace MYNAME\MYBUNDLE\Menu;
interface MenuComponent {
public function build();
}
Menu:
namespace MYNAME\MYBUNDLE\Menu;
class Menu implements MenuComponent
{
private $children;
private $templating;
public function __construct($templating)
{
$this->templating = $templating;
}
public function add(MenuComponent $component)
{
$component->setTemplating($this->templating);
$this->children[] = $component;
}
public function build()
{
return $this->templating->render('MYBUNDLE:Menu:menu.html.twig', array("menu_items" => $this->children));
}
}
Menu Contains the MenuComponents and will render the menu first, in each MenuItem it's build() method is called.
I think it's better to remove the rendering logic from my MenuItem entity and place this somewhere else, however i can't figure out on how to do this properly within this design pattern.
Any help or suggestion is appreciated.