A newbie question ...
To open the first page (index) I have the following in a GamesController:
public function index()
{
//show a listing of games
$games = Game::all();
return view('index',['games'=> $games]);
}
which works fine. I have at the top of the controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Game;
The model is simply
namespace App;
use Illuminate\Database\Eloquent\Model;
class Game extends Model
{
//
}
In my index page I have a simple list from the db with an edit button which has the code:
Edit
In my controller have the following:
public function edit(Request $request)
{
$games = Game::find($request->id);
return view('edit',['games'=>$games]);
}
but when I press the edit button I get
do not put
Route::model('game','Game');
at the beginning of routes
Related
Is there any workaround to link a new action, in a CRUD controller made with EasyAdmin 4.x , to an action in another CRUD controller with which it has a OneToMany relation ?
class FirstEntityCrudController extends AbstractCrudController
{
...
public function configureActions(Actions $actions): Actions
{
return $actions
->add(Crud::PAGE_INDEX, Action::new('add-second-entity','Add a second entity')
->linkToCrudAction(Action::NEW ???)
)
;
}
}
The docs say that I can use:
linkToCrudAction(): to execute some method of the current CRUD controller;
But there seems to be no indication on how to "execute some method of a different CRUD controller".
Note:
There is a sneaky way around it but it doesn't seem healthy :
->linkToUrl('the url to the desired action')
Using:
PHP 8.1
Symfony 5.4
EasyAdmin 4.x
Following #Ruban's comment, and as EasyAdminBundle's docs mention, we can generate a URL for the desired action using the AdminUrlGenerator class as follow:
class FirstEntityCrudController extends AbstractCrudController
{
private $adminUrlGenerator;
public function __construct(AdminUrlGenerator $adminUrlGenerator)
{
$this->adminUrlGenerator = $adminUrlGenerator;
}
...
public function configureActions(Actions $actions): Actions
{
//The magic happens here 👇
$url = $this->adminUrlGenerator
->setController(SecondEntityCrudController::class)
->setAction(Action::NEW)
->generateUrl();
return $actions
//->...
->add(Crud::PAGE_INDEX,
Action::new('add-second-entity', 'Add second entity')
->linkToUrl($url)
);
}
}
This worked for me.
My Controller has a Factory that gives it a Form
$formManager = $container->get('FormElementManager');
return new MyController(
$formManager->get(MyForm::class)
);
My Form has also a Factory that gives it an AuthenticationService
return new MyForm(
$container->get(AuthenticationService::class)
);
That way I can check in the form if the user has identity.
But how can i redirect him from the form?
Just like in a Controller?
if(!$authService->hasIdentity()) {
return $this->redirect()->toRoute('myRoute);
}
Or how can i redirect from a (Controller and/or Form) Factory?
A possible solution for your issue could be the possibilty of using the build method with the factory call.
You haven 't shown your factories, so I will use some standard examples, which explain the solution.
The first approach is not injecting the whole form to the controller. Instead just inject the form element manager. So you can use the build method of the factory inside your controller.
The controller factory
namespace Application\Controller\Factory;
use Application\Controller\YourController;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class YourControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$formElementManager = $container->get('FormElementManager');
return new YourController($formElementManager);
}
}
This differs from your original factory. Only the form element manager is injected to the controller. This holds a few advantages for you. One of this is the build method of the manager.
The Controller
namespace Application\Controller;
class YourController extends AbstractActionController
{
protected $formElementManager;
public function __construct($formElementManager)
{
$this->formElementManager = $formElementManager;
}
public function indexAction()
{
$user = $this->currentUser();
if ($user === null) {
$this->redirect('to/somewhere/the/user/belongs');
}
// here 's the magic!
$form = $this->formElementManager->build(YourForm::class, [
'userID' => $user->getUserId(),
]);
// some form stuff follows here
}
}
As the form was not injected directly to your controller but the form element manager, you can use the form element manager instead inside the controller. This offers you the opportunity to use the build function. With this function you can add some options to your form factory. In this case I 'm using the user id for the form factory.
If there 's no valid user, no form will be created because an exception is thrown before.
The Form Factory
The form factory creates a new instance of your form. All needed dependencies should be created in the factory. How the build function works here, I 'll explain later in the answer.
namespace Application\Form\Factory;
use Application\Form\YourForm;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class YourFormFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$selectOptions = [];
if ($options !== null) {
if (isset($options['userID])) {
$tablegateway = $container->get(YourTableGateway::class);
$selectOptions = $tablegateway->findOptionsByUserId($options['userID]);
}
}
$form = $container->get(YourForm::class);
if (count($selectOptions))
$form->get('YourSelectElement')->setValueOptions($selectOptions);
return $form;
}
}
This factory does all you need. Via the build method you hand over the user id. If a user id is present a table gateway is created from wich you retrieve select options by the given user id. These options will be set to the form field. This logic is kept in the factory to keep the form class itself clean and simple.
With this solution you don 't need the auth service in your form. Your form is only generated when a valid user id is given. Your form instance will not crash, if there 's no user id given. The only conceivable case could be a form with default or no select options for the specific field.
Hope this helps a bit.
I have a test class that reports an undefined variable and I cannot seem to understand what the issue is.
Basically the listener below is suppose to listen to an application boot event documented in the class below:
<?php
namespace Colleen\Core\Event\Application;
final class ApplicationBootedEvents
{
const APP_BOOTED = 'application.booted';
}
My event class is as shown below which receives an instance of the application itself.
<?php
namespace Colleen\Core\Event\Application;
use Symfony\Component\EventDispatcher\Event;
use Colleen\Core\Application;
/**
* The application.booted event is dispatched each time
* an application instance is created in the system.
*
*/
class ApplicationBootedEvent extends Event
{
protected $app;
public function __construct(Application $app)
{
$this->app = $app;
}
public function getApplication()
{
return $app;
}
}
These two classes to me look perfect according to Symfony's documentation on the Event Dispatcher Component. Following is the listener class that is suppose to listen to ApplicationBootedEvents::APP_BOOTED event.
<?php
namespace Colleen\Core\Event\Application\Listener;
use Colleen\Core\Event\Application\ApplicationBootedEvent;
class ApplicationBootedListener
{
public function onBoot(ApplicationBootedEvent $event)
{
$container = $event->getApplication()->getContainer();
$container->set('class.dispatcher', '\\Symfony\\Component\\EventDispatcher\\EventDispatcher');
}
}
The Listener class does nothing at the moment and my test case is to test whether the "class.dispatcher" key exist on my container which simple extends Pimple and is made available through the Application Object.
Below is my test that shows how these will eventually be used in my front controller or any class that stands between them and the front controller.
<?php
namespace Colleen\Qa\Core\Event\Application\Listener;
use Colleen\Core\Event\Application\Listener\ApplicationBootedListener;
use Colleen\Core\Event\Application\ApplicationBootedEvents;
use Colleen\Core\Event\Application\ApplicationBootedEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Colleen\Core\Container\Container;
use Colleen\Core\Application;
class AppliocationBootedListenerTest extends \PHPUnit_Framework_TestCase
{
public function testApplicationBootListener()
{
$dispatcher = new EventDispatcher();
$dispatcher->addListener(
ApplicationBootedEvents::APP_BOOTED, array(
new ApplicationBootedListener(), 'onBoot'
));
$app = $dispatcher->dispatch(ApplicationBootedEvents::APP_BOOTED, new ApplicationBootedEvent(new Application(new Container())))->getApplication();
$expected = '\\Symfony\\Component\\EventDispatcher\\EventDispatcher';
$actual = $app->getContainer()->get('class.dispatcher');
$this->assertSame($expected, $actual);
}
}
The idea is to test whether the Listener gets called and if it is able to feed our application object's container with all the necesary objects we will need to get our web framework to work.
Below is the output I get as a result if running this test case.
There's an error in your ApplicationBootedEvent.php file, on line 24 as the stack trace suggested..
Change
public function getApplication()
{
return $app;
}
To
public function getApplication()
{
return $this->app;
}
in my symfony2 project i need call the same action in many controllers and this action should return a very simple php array that then will be passed to a twig template by these controllers. How can i do it?
A pratical example can explain my situation better.
1.shared controller
// Acme/DemoBundle/Controller/MetasController
class MetasController extends Controller {
public function metasAction() {
$myArray= array();
return $myAarray;
}
}
page render controller
// Acme/DemoBundle/Controller/PageController
class PageController extends Controller {
protected $property = "test";
public function indexAction() {
$metas= $this->forward('AcmeDemoBundle:Metas:metas');
return $this->render('AcmeDemoBundle:Page:index.html.twig', array('property'=>property, 'metas'=>$metas));
}
}
when i do this i get an error: the controller must be a response array given.
You should create a service
// Acme/DemoBundle/Controller/MetasController
class MetasController {
public function metasAction() {
$myArray= array();
return $myAarray;
}
}
declare as service in Acme\DemoBundle\Resources\config\services.yml
services:
demo.metas:
class: "Acme\DemoBundle\Controller\MetasController"
Then you can use it in any other controller
// Acme/DemoBundle/Controller/PageController
class PageController extends Controller {
protected $property = "test";
public function indexAction() {
$metas= $this->get('demo.metas')->metas();
return $this->render('AcmeDemoBundle:Page:index.html.twig', array('property'=>property, 'metas'=>$metas));
}
}
In your action controller :
<?php
...
$arrayExample = array();
return $this->render('ExampleBundle:ExampleFolder:exampleTemplate', array('myArray' => $arrayExample));
And in your twig template now you have access to your array using myArray
Example :
{% for data in myArray %}
...
{% endfor %}
Try this :
use Symfony\Component\HttpFoundation\Response;
public function indexAction()
{
...
$content = $this->renderView(
'AcmeDemoBundle:Page:index.html.twig',
array('property'=> $property,
'metas' => $metas
));
return new Response($content);
}
Yes, you can register your controller as a service as it said above but I would recommend to isolate this logic in a different place. It might be a service but not controller.
As I understand you need the same array in several places. So, it might be some class registered as service or some simple class with static method providing this array. In this case your code will be much cleaner.
If you need this array only in view you can define custom twig method which will return array you need. If this array might be different time to time (if it might depend on some data) you can pass entity manager to the service providing this array or to the twig extension.
(The best use of controllers is to be just a proxy between view and data layer. It's not a good idea to use it for such purposes as you described (in my opinion of course).)
I understand that the $view can be accessed within a rendered template file, but I wonder if there is anyway I can get it elsewhere?
The reason is that I'm building SF2 on top of a classic/legacy system and there are certain templates that cannot be rendered with the normal render method. However, I still want to be able to access the view helper inside those template, so I want to init a global $view which can be used in these legacy templates
These helpers are provided in the PhpEngine class in the Templating component (docs). You can acces this class with the templating.engine.php service (you need to have PHP enabled as a templating engine).
From a controller it will look like this:
// ...
public function fooAction(...)
{
// ...
$template = $this->get('templating.engine.php')->render(...);
}
If you are using another class, you should use DI:
// src/Acme/DemoBundle/Foo.php
namespace Acme\DemoBundle;
use Symfony\Component\Templating\EngineInterface;
class Foo
{
private $templating;
public function __construct(EngineInterface $templating)
{
$this->templating = $templating;
}
public function bar()
{
// ...
$template = $this->templating->render(...);
}
}
// app/config.yml
services:
acme_demo:
foo:
class: Acme\DemoBundle\Foo
arguments: [#templating.engine.php]
More about DI in the docs.