I using symfony and have template with array with some entities array, and need create in for in check box for all entity and when checked some entities and click ready go to action with ids(from all check box) - example - taskExecution.id
I don't used symfony form with type entity because taskExecutions complicated DTO, from this DTO i need only id for to send on another action
$taskExecutions = $this->getTaskExecution()
->getTaskExecutionByFilter($form->getData());
return [
'form' => $form->createView(),
'taskExecutions' => $taskExecutions
];
{% for taskExecution in taskExecutions %}
<input class="searchType" type="checkbox" name="SharingNotification" id={{ taskExecution.id }}>
<label class="searchtype2label">{{ taskExecution.id }}</label>
</input>
{% endfor %}
{% javascripts
'#EconomyBundle/Resources/public/js/check-task-executions.js'
filter='?yui_js' combine=true %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
add js
$('.searchType').click(function() {
alert($(this).attr('id'));
if(this.checked){
$.ajax({
type: "POST",
url: '/manage/outbound_invoices/task_executions/ids',
data: $(this).attr('id'), //--> send id of checked checkbox on other page
success: function(data) {
alert('it worked');
alert(data);
$('#container').html(data);
},
error: function() {
alert('it broke');
},
complete: function() {
alert('it completed');
}
});
}
});
this my action
/**
* give task executions ids for created row.
*
* #Route("/manage/outbound_invoices/task_executions/ids", name="ids_task_executions_")
* #Method({"POST", "GET"})
*/
public function getIdsTaskExecutionsAction(Request $request)
{
$ids = $request->get('ids');
}
I don't know js, help please for understand how get check box value (1 or 0) and entity id parameter and send to another action
I don't think you need javascript for that. Instead you should have a look to the Symfony doc on how to use a form without data_class
your form will looks like :
<?php
class TaskExecutionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('taskExecution', EntityType::class, array(
'class' => 'AppBundle/TaskExecution',
'expanded' => true,
'multiple' => true
))
->add('submit', SubmitType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return 'execution_task_type';
}
}
And in your controller:
<?php
/**
* give task executions ids for created row.
*
* #Route("/manage/outbound_invoices/task_executions/ids", name="ids_task_executions_")
* #Method({"POST", "GET"})
*/
public function getIdsTaskExecutionsAction(Request $request)
{
$form = $this->createForm(TaskExecutionType::class, null, array(
'method' => 'POST',
'action' => 'ids_task_executions'
));
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData(); //this will be an array of all the TaskExecution entities you selected
//your own logic
}
return $this->render('template.html.twig', array(
'form' => $form->createView()
));
}
Related
I am new to laravel and vue.js. I am fetching data from API using resources. The idea is am using 2 div's GOAL and VALUE.
The Value should be updated when there is a change in the value from the server through PUSHER..
without refreshing the page.
here is my code
1.model
class Progress extends Model
{
protected $fillable = [
'id', 'name', 'goal', 'description'
];
public $timestamps = false;
}
2.Controller
class ProgressController extends Controller
{
public function index()
{
return ProgressResource::collection(Progress::paginate(4));
event(new UpdatedValue());
}
}
3.Resource.php
class ProgressResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return[
'the_custom_id' => $this->id,
'name' => $this->name,
'goal' => $this->goal,
'description' => $this->description,
'value' => ProgressResource::mockData($this->goal),
];
}
public static function mockData($goal=1000)
{
// 0 to $goal takes 17 minutes
$multiplier = ($goal + 7) / 1000;
$count = substr(time(), -3);
return intval(round($multiplier * $count, 0));
}
}
4.Events
class UpdatedValue implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $value, $goal;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($value)
{
//$this->progress = ProgressResource::collection(Progress::paginate(4));
$this->value = ProgressResource::mockData($this->goal);
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('progress');
}
}
5.Components/Front.vue
<template>
<div class="container">
<h1> Progress </h1>
<div class= "progress" v-for = "progressdata in progress" v-bind:id="progressdata.id">
<div id="div1">{{ progressdata.goal }}</div>
<div id="div2" class="value">{{ progressdata.value }}</div>
</div>
</div>
</template>
<script>
export default {
data: function() {
return {
progress: [],
}
},
mounted() {
this.loadContents();
this.listen();
},
methods: {
loadContents: function() {
//load Api
axios.get('/api/progress')
.then((response) => {
this.progress = response.data.data;
})
.catch(function (error) {
console.log(error);
});
},
listen(){
Echo.channel('progress')
.listen('UpdatedValue', (e) =>{
this.value = e.value;
console.log(this.value);
//console.log(e);
});
}
}
}
</script>
6.BLade.php
<div id ="app">
<front-page ></front-page>
</div>
<script src = "{{ mix('js/app.js') }}"></script>
</body>
Once the event has been triggered, the value should be updated in the front end without refreshing the page. I have installed PUSHER PACKAGES , PUSHER-JS AND Laravel Echo THROUGH NPM.I couldn't get the value which is updated through event in the front end..Could anyone help to solve this issue?.
Thanks.
loadContents: function() {
//load Api
let self = this ; // we are storing VueComponent object here.Cause this wont work in callback it will be undefined.
axios.get('/api/progress')
.then((response) => {
self.progress = response.data.data;
})
.catch(function (error) {
console.log(error);
});
You didn't define value in data properties. Define Value there and use self object to store this(object).
data: function() {
return {
progress: [],
value:null,
}
},
I'm trying to add a new field to the shipping address in Magento 2.3.4.
I would like to add it "Magento way" that's why I used these tutorials:
https://devdocs.magento.com/guides/v2.3/howdoi/checkout/checkout_new_field.html
https://www.edmondscommerce.co.uk/handbook/Platforms/Magento-2/Guides/Custom-Shipping-Address-Field/
The field appears correctly on the frontend, but after adding new address and filling that field and click "Ship here":
I got that error:
Here is my code:
1) At first, I thought that it is not necessary (this step is not in Magento 2 devdocs but appears in second tutorial) - app/code/Company/Module/Setup/Patch/Data/AddVipCodeAttribute.php:
<?php
namespace Company\Module\Setup\Patch\Data;
use Magento\Catalog\Model\Product;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
class AddVipCodeAttribute implements DataPatchInterface, PatchRevertableInterface
{
/**
* #var ModuleDataSetupInterface
*/
private $moduleDataSetup;
/**
* #var EavSetupFactory
*/
private $eavSetupFactory;
/**
* #var CustomerSetupFactory
*/
private $customerSetupFactory;
/**
* Constructor
*
* #param ModuleDataSetupInterface $moduleDataSetup
* #param EavSetupFactory $eavSetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
EavSetupFactory $eavSetupFactory,
CustomerSetupFactory $customerSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->eavSetupFactory = $eavSetupFactory;
$this->customerSetupFactory = $customerSetupFactory;
}
/**
* #inheritdoc
*/
public function apply()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** #var CustomerSetup $customerSetup */
$customerSetup = $this->customerSetupFactory->create(['setup' => $this->moduleDataSetup]);
$customerSetup->addAttribute('customer_address', 'vip_code', [
'label' => 'Vip account code',
'input' => 'text',
'type' => Table::TYPE_TEXT,
'source' => '',
'required' => false,
'position' => 333,
'visible' => true,
'system' => false,
'is_used_in_grid' => false,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => false,
'is_searchable_in_grid' => false,
'backend' => ''
]);
$attribute = $customerSetup->getEavConfig()->getAttribute('customer_address', 'vip_code')
->addData(['used_in_forms' => [
'adminhtml_customer_address',
'adminhtml_customer',
'customer_address_edit',
'customer_register_address',
'customer_address',
]]);
$attribute->save();
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* #inheritDoc
*/
public function revert()
{
}
/**
* #inheritdoc
*/
public function getAliases()
{
return [];
}
/**
* #inheritdoc
*/
public static function getDependencies()
{
return [];
}
}
2)I created a plugin class:
etc/frontend/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
<plugin name="add_custom_field_checkout_form" type="Company\Module\Plugin\Checkout\LayoutProcessor" sortOrder="100"/>
</type>
</config>
And layout processor app/code/Company/Module/Plugin/Checkout/LayoutProcessor.php
<?php
namespace Company\Module\Plugin\Checkout;
use Magento\Checkout\Block\Checkout\LayoutProcessor as LayoutProcessorCore;
use Magento\Customer\Model\Session;
class LayoutProcessor
{
/**
* #var Session
*/
protected $session;
public function __construct(
Session $session
) {
$this->session = $session;
}
/**
* #param LayoutProcessorCore $subject
* #param array $jsLayout
*
* #return array
*/
public function afterProcess(
LayoutProcessorCore $subject,
array $jsLayout
) {
$customAttributeCode = 'vip_code';
$customField = [
'component' => 'Magento_Ui/js/form/element/abstract',
'config' => [
// customScope is used to group elements within a single form (e.g. they can be validated separately)
'customScope' => 'shippingAddress.custom_attributes',
'customEntry' => null,
'template' => 'ui/form/field',
'elementTmpl' => 'ui/form/element/input',
'tooltip' => [
'description' => 'Vip accounts code. Example: 123123123ASD',
],
],
'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
'label' => 'Vip code',
'provider' => 'checkoutProvider',
'sortOrder' => 0,
'validation' => [
'required-entry' => false
],
'options' => [],
'filterBy' => null,
'customEntry' => null,
'visible' => true,
];
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField;
return $jsLayout;
}
}
3)JS files:
app/code/Company/Module/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/set-shipping-information': {
'Company_Module/js/add-new-field': true
}
}
}
};
app/code/Company/Module/view/frontend/web/js/add-new-field.js
/*jshint browser:true jquery:true*/
/*global alert*/
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper, quote) {
'use strict';
return function (setShippingInformationAction) {
return wrapper.wrap(setShippingInformationAction, function (originalAction) {
var shippingAddress = quote.shippingAddress();
if (shippingAddress['extension_attributes'] === undefined) {
shippingAddress['extension_attributes'] = {};
}
shippingAddress['extension_attributes']['vip_code'] = shippingAddress.customAttributes['vip_code'];
console.log(shippingAddress);
// pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
return originalAction();
});
};
});
[UPDATE]
app/code/BartCompany/VipAccounts/etc/extension_attributes.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
<attribute code="vip_code" type="string" />
</extension_attributes>
</config>
Can anybody help me with that, I'm working on it almost a week, and tried a lot of solutions, but it is for me very important to do that by custom_attributes field.
I will be grateful, thank You.
I think i found the issue, here is a follow:
https://github.com/magento/magento2/issues/26740
I'm trying to create a dynamic 2-step form using Jquery where in "step 1", I want to submit the form data without refreshing my page so that I can hide my html division containing my form and show the other representing my step 2 using Jquery.
The problem is that I'm using a collection of forms in my controller action like this:
public function indexAction(Request $request)
{
$user = $this->getUser();
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository('ATPlatformBundle:NoteDeFrais');
$form = $this->get('form.factory')->createBuilder(FormType::class)
->add('ndf', CollectionType::class,array(
'entry_type' => NoteDeFraisType::class,
'label' => false,
'allow_add' => true,
'allow_delete' => true,
))
->getForm();
And I'm getting the forms data submitted from like this:
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()
&& isset($_POST['next_button'])) {
$notesDeFrais = $form['ndf']->getData();
foreach ($notesDeFrais as $ndf) {
$ndf->setUser($user);
$em->persist($ndf);
}
$em->flush();
}
elseif (isset($_POST['validate_button'])) {
foreach ($listNdf as $ndf) {
$ndf->setSubmitted(true);
}
$em->flush();
}
So what I wanted to know is how to send my data via an ajax request and how to get them from my action. So far I tried to proceed like this but it (logically) doesn't work.
$("div#bloc_validation").css("display", "none");
$("#next_button").click(function(){
$(".form_ndf").each(function(){
$.post("{{ path('platform_homepage') }}",
{ndf: $(this).serialize()}, //My issue is here
function(){
alert('SUCCESS!');
}
);
});
$("div#form_bloc ").css("display", "none");
$("div#bloc_validation").css("display", "block");
});
Do you have any ideas ? Thanks in advance
The most basic approach is this:
add a javascripts block in your twig file with the content as below.
Change appbundle_blog in the first line inside the .ready() function in the name of your form. (Inspect your html to find it).
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(edit_form) }}
{{ form_widget(edit_form) }}
<input type="submit" value="Save Changes" />
{{ form_end(edit_form) }}
{% endblock %}
{% block javascripts %}
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
<script>
$(document).ready( function() {
var form = $('form[name=appbundle_blog]');
form.submit( function(e) {
e.preventDefault();
$.ajax( {
type: "POST",
url: form.attr( 'action' ),
data: form.serialize(),
success: function( response ) {
console.log( response );
}
});
});
});
</script>
{% endblock %}
If the form has been submitted you have to answer to an AJAX request. Therefore you could render another template..
/**
* Displays a form to edit an existing blog entity.
*
* #Route("/{id}/edit", name="blog_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Blog $blog)
{
$editForm = $this->createForm('AppBundle\Form\BlogType', $blog);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
/* render some new content */
return $this->render('blog/ajax.html.twig', array(
'blog' => $blog,
));
}
return $this->render('blog/edit.html.twig', array(
'blog' => $blog,
'edit_form' => $editForm->createView(),
));
Or answer in JSON:
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Displays a form to edit an existing blog entity.
*
* #Route("/{id}/edit", name="blog_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Blog $blog)
{
$editForm = $this->createForm('AppBundle\Form\BlogType', $blog);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return new JsonResponse(array(
'status' => 'success',
// ...
));
}
return $this->render('blog/edit.html.twig', array(
'blog' => $blog,
'edit_form' => $editForm->createView(),
));
}
If you want you can even test if the request is an AJAX request or not:
if($request->isXmlHttpRequest()) {
// yes it is AJAX
}
I'm on symfony and I use collection form to make a site to reserve tickets for a show.
The interface is simple, The user select the number of tickets he wants, then it display as much form prototype as tickets required. This part works well for me.
But I would like to display only 2 field ( name and surname ) not the age field ( It will be asked in another part of my form ) of my billet entity.
In the documentation they explain that you can display one field only (if I understand well ) :
<ul class="billets" data-prototype="{{ form_widget(form.billets.vars.prototype.surname)|e }}">
Or the all entity fields :
<ul class="billets" data-prototype="{{ form_widget(form.billets.vars.prototype)|e }}">
But not 2 fields, because when I try this, it display only the first field :
<ul class="billets" data-prototype="{{ form_widget(form.billets.vars.prototype.name)|e }}">
<ul class="billets" data-prototype="{{ form_widget(form.billets.vars.prototype.surname)|e }}">
Here is my billet type :
class BilletType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('surname', TextType::class)
->add('name', TextType::class)
->add('dateOfBirth', BirthdayType::class)
;
}
}
to avoid rendering fields I do it this way with option : {'render_rest': false}) }
{{ form_widget(edit_form._token) }}// mandatory but hidden
{{ form_end(edit_form, {'render_rest': false}) }} //closing the form
This way only fields specified in twig are rendered.
Try this:
Create custom BilletType like this:
class CustomBilletType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('surname', TextType::class)
->add('name', TextType::class)
;
}
}
Imbricate this CustomFormType in your main form:
class BilletType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('billets', CollectionType::class, array(
'entry_type' => new CustomBilletType,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false, //use this for set dateOfBirth for all billet in your collection
))
->add('dateOfBirth', BirthdayType::class)
;
}
}
You will have your collection of billet with only dateOfBirth field.
For persist your dateOfBirth for all your billet, see in entity like this:
/**
* Constructor
*/
public function __construct()
{
$this->billets = new ArrayCollection();
}
/**
* Add billets
*
* #param \AppBundle\Entity\Billet $billet
* #return Billet
*/
public function addBillet(Billet $billet)
{
$this->billets[] = $billet;
$billet->setDateOfBirth($this->dateOfBirth); //Set the same date for all your billet
return $this;
}
I hope I understand your problem..
I have two entities: Issue and Notes, and an issue can have multiple notes. I defined them like this:
class Issue {
// ...
/**
* #ORM\OneToMany(targetEntity="Note", mappedBy="issue")
*/
protected $notes;
public function getNotes() {
return $this->notes->toArray();
}
public function addNote($note) {
if (!$this->notes->contains($note)) $this->notes->add($note);
}
public function removeNote($note) {
if ($this->notes->contains($note)) $this->notes->removeElement($note);
}
}
class Note {
// ...
/**
* #ORM\Column(type="string", nullable=false)
*/
protected $description;
/**
* #ORM\ManyToOne(targetEntity="Issue", inversedBy="notes")
* #ORM\JoinColumn(name="issue", referencedColumnName="id", nullable=false)
*/
protected $issue;
public function getDescription() // ...
public function setDescription() // ...
public function getIssue() // ...
public function setIssue() // ...
}
I defined an IssueType to create a form that embeds a NoteType form:
class IssueType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
// ...
->add('notes', 'collection', ['type' => new NoteType()]);
}
}
class NoteType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('description', 'textarea');
}
}
This works well when I want to create a new issue because the array of notes only contains one (blank) note created by the IssueController, which the user can fill in using the form. However, once the issue is created, I've got a view issue page that shows all the notes, and at the bottom there is a form to add a new note. The problem is that the collection form creates an input for a new (blank) note as well as for all the previous notes (see image below).
Is there any way I can only include the input for the new (blank) note form using Symfony, or do I need to remove the previous notes with JavaScript?
EDIT:
Here's the code for the IssueController:
public function newAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$issue = new Issue();
$note = new Note();
$issue->addNote($note);
$note->setIssue($issue);
$form = $this->createForm('issue', $issue);
$form->handleRequest($request);
// ...
if ($form->isValid()) {
$em->persist($issue);
$em->persist($note);
$em->flush();
return $this->redirectToRoute(...);
}
return $this->render('/issue/new.html.twig', [
'issue' => $issue,
'form' => $form->createView()
]);
}
The note edit boxes appear because your form is instructing to create an editable collection for the one-to-many relationship. Putting something in a form type means it's typically going to be editable, or at the very least presented as form.
If you want your form to only be able to ADD a new note, you must remove that collection property of the form.
Instead of
->add('notes', 'collection', ['type' => new NoteType()]);
have
->add('newNote', 'textarea', ['label' => 'Add note', 'mapped' => false];
Your controller will need amendments too.
public function newAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$issue = new Issue();
$form = $this->createForm('issue', $issue);
$form->handleRequest($request);
if ($form->isValid()) {
if ($newNote = trim($form['newNote']->getData())) {
$note = new Note();
$issue->addNote($note);
$note->setIssue($issue);
$note->setDescription($newNote);
$em->persist($note); // Probably not needed as you're cascading persist
}
$em->persist($issue);
$em->flush();
return $this->redirectToRoute(...);
}
return $this->render('/issue/new.html.twig', [
'issue' => $issue,
'form' => $form->createView()
]);
}
This will only show an input for a new note. To show existing notes you'll need to do this in your view, for example:
<h2>Notes</h2>
{% for note in issue.notes if note.description %}
<p>{{ loop.index }}: {{ note.description }}</p>
{% else %}
<p>No notes have been added for this issue.</p>
{% endfor %}