Form collection with last array element - javascript

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 %}

Related

laravel voyager range slider

what should i do to change this form field to be a range slider with percentage in laravel voyager
i dont know where to change the code in voyager, or maybe i should make a custom formfield?`<?php
namespace App\FormFields;
use TCG\Voyager\FormFields\AbstractHandler;
class NumberFormField extends AbstractHandler
{
protected $codename = 'number';
public function createContent($row, $dataType, $dataTypeContent, $options)
{
return view('formfields.number', [
'row' => $row,
'options' => $options,
'dataType' => $dataType,
'dataTypeContent' => $dataTypeContent
]);
}
}`

Laravel cannot retrieve data from multiple model with Ajax

Hi I am beginner with Ajax at the Laravel. I wanted to fetch data with Laravel Eagerload function to my blade modal.
This is my Expenses Model
protected $fillable = [
'name',
'surname',
'email',
'password',
'status',
'role',
];
protected $hidden = [
'password',
'remember_token',
];
public function Expense () {
return $this->hasMany(ExpensesModel::class);
}
This is my Expenses Model
`
{
use HasFactory;
protected $table = 'expenses';
protected $fillable = [
'id',
'emp_id',
'expense_input_date',
'expense_type',
'expense_category',
'reason',
'status'
];
public function UserExpense () {
return $this->belongsTo(User::class, 'emp_id' );
}
My controller
This is My controller function
public function edit (Request $request) {
$req_id = array('id' => $request->id);
if($request->ajax()) {
$employee = ExpensesModel::with('UserExpense')->where('id' ,$req_id)->first();
return response()->json($employee);
}
}
This is my blade script
`
function editFunc(id){
$.ajax({
type:"POST",
url: "{{ url('/expenses/advancedtable/edit') }}",
data: { id: id },
dataType: 'json',
success: function(res){
$('#EmployeeModal').html("Edit Employee");
$('#employee-modal').modal('show');
$('#id').val(res.id);
$('#emp_id').val(res.name);
$('#expense_input_date').val(res.expense_input_date);
$('#expense_type').val(res.expense_type);
$('#expense_category').val(res.expense_category);
$('#expense_slip_no').val(res.expense_slip_no);
$('#expense_amount').val(res.expense_amount);
$('#currency').val(res.currency);
$('#description').val(res.description);
}
});
}
I tried everyting but it does not work. I wanted to retrive user name from User Model by using foreign key on the Expenses model emp_id.
is there something I missed somewhere can you help me with this.
Thank you.
Here how its work.
First of all change relationship in your User and Expenses model like this.
// User Model
public function userExpense() {
return $this->hasMany(ExpensesModel::class,'emp_id','id');
}
// ExpensesModel
public function user() {
return $this->hasOne(User::class,'id','emp_id');
}
Then change your controller function.
// controller function
public function edit (Request $request) {
$req_id = $request->id;
$employeeExpense = ExpensesModel::with('user')->where('id' ,$req_id)->first();
return response()->json($employeeExpense);
}
Then change your ajax sucess function.
// ajax sucsess function
success: function(res) {
console.log(res); // to view your response from controller in webbrowser's console
$('#EmployeeModal').html("Edit Employee");
$('#employee-modal').modal('show');
$('#id').val(res.id);
$('#emp_id').val(res.user.name); // it will print user name
$('#expense_input_date').val(res.expense_input_date);
$('#expense_type').val(res.expense_type);
$('#expense_category').val(res.expense_category);
$('#expense_slip_no').val(res.expense_slip_no);
$('#expense_amount').val(res.expense_amount);
$('#currency').val(res.currency);
$('#description').val(res.description);
}
when you use 'with' eloqunt method it will add relationship function name to your query result, so you want to get user details then you should be do like res.user.userfield this is applicable for hasOne only.
For other relationship you will refer to this https://laravel.com/docs/9.x/eloquent-relationships

how to get the value in a particular div once the event is triggered in laravel?

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,
}
},

Symfony check box with action

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()
));
}

Many prototype for collection type

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..

Categories

Resources