I haven't found any comprehensive answer at the moment ..
I would like to learn how to change a select option based on the choices of another select.
eg.
Category One-to-Many SubCategory
I select an option from the Category and SubCategory select content changes.
Could you give me a hand?
First you need to use the routing url to pass the control to the action using jquery
eg
$('# category id').change(function(){
var Id = $('#category id').val();
var url = Routing.generate('route_to_retrieve_subcategory', { 'Id': Id });
$.post(url,
{ 'Id': Id
},function(data){
$('#subcategoryId').html(data);
},"text");
}
});
In controller
/**
* #Route("subcategory/{Id}",name="route_to_retrieve_subcategory" )
* #Template()
*/
public function getSubcategoryAction($Id)
{
//code
return new Response($subcategoryList, 200);
}
Note:
the route must be listed in routing.yml file
route_to_retrieve_subcategory:
pattern: /route_to_retrieve_subcategory/{Id}
defaults: {_controller: YourBundle:YourController:getSubcategory}
options:
expose: true
in the end I decided to use this method:
javascript:
$('select[name*="[category][category]"]').prop('selected', true).change(function(){
var Id = $(this).val();
var url = Routing.generate('route_to_retrieve_subcategory');
$.post(url,
{ 'idCat': Id
}, function(results){
var sub = $('select[name*="[category][category]"]').parent().find('select[name*="[subCategory][]"]');
sub.empty();
$.each(results , function(key, value) {
sub
.append($("<option></option>")
.attr("value",value.id)
.text(value.subCategory));
});
});
});
controller:
public function getSubcategoryAction(Request $request)
{
$Id = $request->get('idCat');
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('MyBusinessBundle:SubCategories')->findSubCategories($Id);
$output = array();
foreach ($entities as $member) {
$output[] = array(
'id' => $member->getId(),
'subCategory' => $member->getSubCategory(),
);
}
$response = new Response();
$response->headers->set('Content-Type', 'application/json');
$response->setContent(json_encode($output));
return $response;
}
route:
route_to_retrieve_subcategory:
pattern: /route_to_retrieve_subcategory
defaults: { _controller: "MyBusinessBundle:ajax:getSubcategory" }
options:
expose: true
I prefer not to pass parameters through the course, I feel that it does not make sense!
A big thank you to shrujan shetty for the inspiration.
Related
I have an Entity named Page that can be a callToAction (boolean) and I would like to display the Page Entity with callToAction == false on one subMenu and the Page Entity with callToAction == true on another subMenu. I have a CRUD for the Page Entity. So the Dashboard would be something like that:
MenuItem::subMenu('Page', 'far fa-file-alt')->setSubItems([
MenuItem::linkToCrud('Page', 'fa fa-alt', Page::class),
MenuItem::linkToCrud('Call To Action', 'fa fa-file-alt', Page::class),
])
But I don't know where to put the dql to display the entities I want (callToAction true or false) and I don't even know if it's possible, but I know it was with Easy Admin 2, that's why I wonder.
I also would like that on the NEW Action, when you're on the Page with callToAction == true, when you create the new Entity Page from here, that the callToAction is set to true immediatly and the User doesn't even see the field. Still don't know if it's possible.
Thanks :)
EDIT: So i've found that I can use createIndexQueryBuilder() to display on the index exactly the entities, and it works well but I don't know how to call two different createIndexQueryBuilder depending of the subMenu we display. I tried doing a custom action and using createQueryBuilder but I don't have the params searchDto, etc:
public function configureActions(Actions $actions): Actions
{
$indexIsCallToAction = Action::new('indexIsCallToAction', 'Index Call To Action', 'fa fa-send')
->linkToCrudAction('indexIsCallToAction');
$actions->add(CRUD::PAGE_INDEX, $indexIsCallToAction);
return $actions;
//return parent::configureActions($actions); // TODO: Change the autogenerated stub
}
public function indexIsCallToAction(AdminContext $context,SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters){
$response = $this->get(EntityRepository::class)->createQueryBuilder($searchDto, $entityDto, $fields, $filters);
return $response;
}
So it doesn't work.
As a dashboard controller is an ordinary controller you can do something like this:
public function __construct(PageRepository $pageRepo)
{
$this->pageRepo = $pageRepo;
}
public function configureMenuItems(): iterable
{
$submenuItems = [];
if (null !== $pages = $this->pageRepo->findBy(["callToAction" => true ])) {
foreach ($pages as $page) {
$submenuItems[] = MenuItem::linkToCrud('Call To Action', 'fa fa-file-alt', Page::class);
}
}
yield MenuItem::subMenu('Page Submenu with callToAction', 'far fa-file-alt')->setSubItems($submenuItems);
$submenuItems = [];
if (null !== $pages = $this->pageRepo->findBy(["callToAction" => false ])) {
foreach ($pages as $page) {
$submenuItems[] = MenuItem::linkToCrud('Page', 'fa fa-alt', Page::class);
}
}
yield MenuItem::subMenu('Other Page Submenu', 'far fa-file-alt')->setSubItems($submenuItems);
}
I have this button and I'd like to make it so when I click on it, there's an ajax that goes directly into my create function in symfony but doesn't display any form (at this point I already have the informations I need). But I have no idea how to get the form that way.
I used to do
$livre = new Livre();
$livre->setUuid(Uuid::v4());
$form = $this->createForm(LivreType::class, $livre);
$form->handleRequest($request);
But obviously I can't use LivreType::class anymore cause I don't need the form.
I keep searching for information about this but I can't find anything
Any ideas?
You'll have multiple way of doing it.
I'm gonna show you a simple way to do it, and try to adapt or find a better way for doing it!
LivreController.php
/**
* #Route(path="/livre/create", methods={POST})
*/
public function createNewLivre(Request $request, EntityManagerInterface $em)
{
$json = $this->getJSON($request);
$newLivre = new Livre();
// Set to your new entity parameters...
$em->persist($newLivre);
$em->flush();
return $this->json([
'message' => 'A new Livre has been added.' // It could also be empty if you don't want to manage anything
]);
}
private function getJSON(Request $request)
{
$data = json_decode($request->getContent(), true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new HttpException(400, 'json invalid');
}
return $data;
}
script.js
let button = document.getElementById('livreCreatorButton');
button.addEventListener("click", e => {
e.preventDefault();
fetch('http://127.0.0.1:8000/livre/create', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: '{}',
})
});
I'm starting with chartjs and i'm already struggling. I want to set my labels with data I get from a query :
public function find4Chart($joueur){
$qb = $this->createQueryBuilder('p');
$qb->join('p.partie','y')
->join('y.game','g')
->select('g.name')
->addSelect('COUNT(p) AS sumParties')
->where('p.joueur = :joueur')
->setParameter('joueur', $joueur)
->groupBY('g.name');
$query = $qb->getQuery();
return $query->getResult();
}
Then I set up the chart like this :
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: [{{ chart.name }}],
But it gives me the following error and I don't know why or what to do here :/
Key "name" for array with keys "0, 1" does not exist.
Thx !
Regarding your own answer - you could change this part:
$arrChart = array();
foreach($chart as $c){
$name = $c['name'];
array_push($arrChart, $name);
}
into this:
$arrChart = array_column($chart, 'name');
You can read more about array_column here
Ok it worked for me this way :
public function show(Joueur $joueur): Response
{
$em = $this->getDoctrine()->getManager();
$chart = $em->getRepository(Played::class)->find4Chart($joueur);
$arrChart = array();
foreach($chart as $c){
$name = $c['name'];
array_push($arrChart, $name);
}
return $this->render('joueur/show.html.twig', [
'joueur' => $joueur,
'chart' => $arrChart,
]);
}
Chart :
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: {{chart | json_encode | raw}},
I don't know if it's the right way to do it but it works, if any of you have a better idea, I'll take it ;)
I asked this on https://github.com/silexphp/Silex/issues/1442 but I have a feeling it isn't necessarily a code problem so I thought I would extend the reach.
upgrading to silex 2 and symfony 3 and all the rest...
routes are included at end of app.php and look something like this...
$app->get('/', 'queue.controller:indexAction')
->after(function (Request $request, Response $response, $app) {
$response->setPublic();
$response->setSharedMaxAge($app['cache']['s-maxage']);
})
->host($app['domains']['xxx'])
->bind('homepage');
this works great in a browser, goto homepage, works fine. If I am running phpunit though... I get the following error when trying to generate a route...
Symfony\Component\Routing\Exception\RouteNotFoundException: Unable to generate a URL for the named route "homepage" as such route does not exist.
and the test looks something like this...
public function testTargetingOnHomepage()
{
$client = $this->createClient();
echo $this->app->url('homepage');
.....
}
i am bewildered as to why my routes are not getting added to app when being executed from phpunit.
test case class has ...
class AdTargetControllerTest extends SomeWebTestCase
and that somewebtestcase.php looks like ...
<?php
namespace theapp\SomeFramework;
use Silex\WebTestCase;
use Fixtures\UserFixture;
use Symfony\Component\BrowserKit\Client;
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
class SomeWebTestCase extends WebTestCase
{
public $youtubeEmbed;
private $mockSessionDir;
public function createApplication()
{
$appDir = __DIR__.'/../../..';
$app = include "$appDir/app.php";
$app['debug'] = true;
$app['session.test'] = true;
$this->mockSessionDir = $app['tmpdir']. '/mocksessions';
$app['session.storage.test'] = function ($app) {
return new MockFileSessionStorage($app['tmpdir']. '/mocksessions');
};
unset($app['exception_handler']);
// Emails get stored in the mail "logger" ... not delivered.
$app['mailer.logger'] = function ($app) {
return new \Someecards\SomeFramework\MessageLogger();
};
// Force silex to use transport and not spooltransport
$app['swiftmailer.use_spool'] = false;
$app["swiftmailer.transport"] = function ($app) {
return new \Swift_Transport_NullTransport($app['swiftmailer.transport.eventdispatcher']);
};
$app->extend('mailer', function ($mailer, $app) {
$mailer->registerPlugin($app['mailer.logger']);
return $mailer;
});
$this->youtubeEmbed = '<iframe width="480" height="270" src="https://www.youtube.com/embed/';
$this->youtubeEmbed .= 'LDtHJYa6xu4?feature=oembed" frameborder="0" allowfullscreen></iframe>';
// using Mockery since this library uses a static method for create
$app['oembedMock'] = \Mockery::mock('alias:Embed\Embed');
$app['oembedMock']
->shouldReceive('create')
->andReturn(
(object) array(
'title' => 'Oembed title',
'description' => 'Oembed Description',
'providerName' => 'YouTube',
'code' => $this->youtubeEmbed,
'type' => 'video',
'author' => 'test',
'authorUrl' => 'test',
'width' => 500,
'height' => 500,
'image' => 'test',
'imageWidth' => 500,
'imageHeight' => 500
)
);
$app->boot();
return $app;
}
there are other functions here but thats the important one i believe.
phpunit bootstraps with ...
<?php
use theapp\SomeFramework\TestingUtil;
// autoload libraries
require_once __DIR__.'/../thirdparty/vendor/autoload.php';
TestingUtil::init();
and then testingutil.php looks like ...
namespace Someecards\SomeFramework;
class TestingUtil
{
public static function init()
{
// This if/else allows the phpunit processIsolation flag to be set to true.
// We're not currently doing that because it slows things down three fold.
// If you see errors about too many open connections/files you can run
// ulimit -n 10000, try to close db connections and log files (couldn't fclose
// these in tearDowns for some reason), or turn on processIsolation.
if (!defined('PHPUNIT_HAS_BOOTSTRAPED')) {
self::bootstrap();
define('PHPUNIT_HAS_BOOTSTRAPED', true);
}
}
public static function bootstrap()
{
$app = require __DIR__.'/../../../app.php';
$app->boot();
$dbOptions = $app['db.options'];
if ($dbOptions['driver'] == 'pdo_sqlite') {
$testdb = $dbOptions['path'];
if (file_exists($testdb)) {
#unlink($testdb);
}
$cacheDriver = $app['orm.em']->getConfiguration()->getMetadataCacheImpl();
$cacheDriver->deleteAll();
$cacheDriver = $app['orm.em']->getConfiguration()->getResultCacheImpl();
$cacheDriver->deleteAll();
$cacheDriver = $app['orm.em']->getConfiguration()->getQueryCacheImpl();
$cacheDriver->deleteAll();
$tool = new \Doctrine\ORM\Tools\SchemaTool($app['orm.em']);
$classes = $app['orm.em']->getMetadataFactory()->getAllMetadata();
$tool->createSchema($classes);
$loader = new \Doctrine\Common\DataFixtures\Loader();
$loader->loadFromDirectory(__DIR__ ."/../../Fixtures");
$purger = new \Doctrine\Common\DataFixtures\Purger\ORMPurger();
$executor = new \Doctrine\Common\DataFixtures\Executor\ORMExecutor($app['orm.em'], $purger);
$executor->execute($loader->getFixtures());
}
register_shutdown_function(
function ($app) {
$path = $app['orm.default_cache']['path'];
if (is_dir($path) === true) {
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $file) {
if (in_array($file->getBasename(), array('.', '..')) !== true) {
if ($file->isDir() === true) {
rmdir($file->getPathName());
} elseif (($file->isFile() === true) || ($file->isLink() === true)) {
unlink($file->getPathname());
}
}
}
rmdir($path);
} elseif ((is_file($path) === true) || (is_link($path) === true)) {
return unlink($path);
}
},
$app
);
}
}
I'm using jeditable plugin for JavaScript and I want to implement it in my Symfony2 project. I want to edit a name with the plugin and that name to be edited in the database, too, not the change to be gone when I refresh the page, but in my case, it's gone. :(
I'm almost sure that the controller shouldn't be in that way and the problem is from it, but how exactly to write it? Here it is:
public function editCategoryAction(Request $request, $id)
{
$category = $this->repository->find($id);
$form = $this->createForm(new CategoryType(), $category);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$this->em->persist($category);
$this->em->flush();
return $this->redirect($this->generateUrl('categories'));
}
}
return $this->render(
'AcmeBudgetTrackerBundle:Categories:categories.html.twig', array(
'form' => $form->createView()));
}
This is my template:
<a href="{{ path('edit_category', { 'id': cat.id}) }}">
<strong class="edit">
{{ cat.name }}
</strong>
</a>
<script>
var token = "{{form._token.vars.value}}";
var path = "{{ path('edit_category', { 'id': cat.id}) }}";
</script>
And this is in the .js file:
(function(){
$('.edit').editable(function (value, settings) {
var data = {};
data[this.id] = value;
data["_token"] = token;
console.log(path);
console.log(data);
$.post(path, data);
return(value);
}, {
indicator:'Saving...'
});
}) ();
The output in the console looks fine:
/BudgetTracker/web/app_dev.php/edit_category/52
Object {: "Edu", _token: "9d29860b59ccafbc265ea12346c91fa7e378cc97"}
but the problem is that nothing is posted to the database and when I hit refresh the change I made is gone.
Can you please help me to solve this? Thanks in advance! :)
I think you don't need to use the form component here, you only want to handle a string. So I'll explain a way to do.
JavaScript:
$('.edit').editable(path);
Controller:
public function editCategoryAction(Category $category)
{
//check if exists/valid
//get the text sent from jeditable
$name = $this->request->get('value');
$category->setName($name);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
//return the name value to jeditable so it can display it
return new Response($name);
}
Twig:
<strong class="edit">{{ cat.name }}</strong>
<script>
var path = "{{ path('edit_category', { 'id': cat.id}) }}";
</script>
jEditable sends edited text named as 'value', so you can get it in the controller. In your controller, you implicitly use paramconverter to get the category from the id in the URL. And that should be OK now.
Note that you can use FORJsRoutingBundle, if you want to avoid mixing twig with javascript in order to access path.