Sonata admin: setting a filter to false by default? - sonata-admin

I am currently using Sonata Admin to generate a datagrid with an entity having a read boolean field. I would like to filter on this property, setting it by default to false.
So, I added the following to my Admin class:
protected $datagridValues = array(
'read' => array('value' => false),
);
Yet, it does not seem to work. The generated select list is the following:
<select id="filter_read_value" name="filter[read][value]" class="span8">
<option value=""></option>
<option value="1">oui</option>
<option value="2">non</option>
</select>
I suppose this is normal, as value for false would be 0, which is the empty option.
So, I used some constants such as:
const STATUS_READ = 1;
const STATUS_UNREAD = 2;
It works, but I am wondering if there is any proper solution to avoid these two unnecessary constants?

You can use getFilterParameters maybe:
<?php
public function getFilterParameters()
{
$this->datagridValues = array_merge(array(
'booleanField' => array(
'value' => '0',
)
),
$this->datagridValues
);
return parent::getFilterParameters();
}

There is a bit too little information, on how you persist your enumeration into the database, but it is pretty standard to have your value constants stored together with the possible options in your type class. There is nothing wrong in it, as long as you never reference your option as an integer.

the best solution would be to use the types of sonata-admin:
<?php
protected $datagridValues = [
'read' => [
'type' => Sonata\CoreBundle\Form\Type\EqualType::TYPE_IS_EQUAL,
'value' => Sonata\CoreBundle\Form\Type\BooleanType::TYPE_NO,
]
];

Related

wherenotin() must be of type array, null given error

so im trying to update my data, and it shows this:
Illuminate\Database\Query\Builder::cleanBindings(): Argument #1 ($bindings) must be of type array, null given
so when i try to look into it, the error is here
$tradeSetup->tags()->whereNotIn('user_tag_id', $request->userTag)->delete();
weird thing is, i already pass the value as array, but the error comes out when i dont want to return any userTag at all.
//form.blade.php
<div class="form-group row">
<label class="col-md-2 col-form-label">#lang('Tags')</label>
<div class="col-md-10">
<select name="userTag[]" class="select2-tag form-control" multiple="multiple">
#foreach($userTags as $key => $userTag)
<option value="{{ $userTag->id }}" {{ $tradeSetup->tags->where('user_tag_id',$userTag->id)->first() ? 'selected' : '' }}>{{ $userTag->name }}</option>
#endforeach
</select>
</div>
</div><!--form-group-->
//TradeSetup model
public function userTag()
{
return $this->hasMany(UserTag::class);
}
public function tags()
{
return $this->morphMany(Tag::class, 'taggable');
}
//TradeSetupController.php
public function update(Request $request, TradeSetup $tradeSetup)
{
$request->validate([
'symbol' => 'required',
'entry_price' => 'required',
'target_price' => 'required',
'stop_loss_price' => 'required',
'notes' => 'required',
// 'tags' => 'required',
'side' => 'required',
'confidence' => 'required',
// 'ratio' => 'required',
]);
$tradeSetups = $this->tradeSetupService->update($request, $tradeSetup);
// dd($request->userTag);
$tradeSetup->tags()->whereNotIn('user_tag_id', $request->userTag)->delete();
foreach($request->userTag as $tag){
if($tradeSetup->tags()->where('user_tag_id', $tag)->exists()){
// if (Tag::where('user_tag_id', $tag )->exists()) {
}else if (Tag::whereHas('userTag')->where('taggable_id', $tradeSetup->id)->where('taggable_type', 'App\Domains\TradeSetup\Models\TradeSetup')->get()){
Tag::create([
'user_tag_id'=> $tag,
'taggable_type' => get_class($tradeSetups),
'taggable_id'=> $tradeSetups->id,
// 'type' => $request->type,
// 'state'=> $request->state,
]);
}
}
return redirect()->route('frontend.user.tradeSetup.index')->withFlashSuccess('TradeSetup updated');
}
i tried to dd($request->userTag) and the result is null
FYI, userTag is not required so the user can just not input any userTag when they store/update.
Maybe u dont have tags <form> and <input type="submit"> to send request into your controller. Must be an action to provide all request info.
If u want to do it dynamic, use axios or XmlHttpRequest in javascript to send request into controller.
It's not a solution, but also u can check csrf protection, there is full information about forms and their protection, check this out
https://laravel.com/docs/9.x/csrf
The method whereNotIn needs an array as second argument. Then no value is selected in your form, the request doesn't contain any data for userTag and so it is null and not an empty array.
But you can simply fix this by defining a default parameter for userTags for example like this:
$tradeSetup->tags()->whereNotIn('user_tag_id', $request->userTag ?: [])->delete();
UPDATE
my mistake, but i dont need to actually change the null to []. i just want to delete all the record in Tag.
if(isset($request->userTag)){
$tradeSetup->tags()->whereNotIn('user_tag_id', $request->userTag)->delete();
}else{
$tradeSetup->tags()->delete();
}

Laravel edit blade checkbox array issue

My Problem I can't get my dynamic checkboxes to save properly in from my edit.blade, they only work if the values are 1, if an unchecked checkbox is submitted via hidden field it will overwrite the next set of checkbox checked values.
My Code
I have a crud resource that takes orders, the form in the create.blade itself has a bunch of dynamic fields that add a new product to the order via 'add-new' button that clones the product fields.
Part of that form is a bunch of days checkboxes that work fine and are stored correcly.
Where I'm Getting Stuck
I've made an edit.blade to be used to correct any mistakes that would be made while creating an order.
To call back the section that refers to the dates checkboxes I've used the following blade syntax (I know its different from create, mainly due to me trying to fix the problem)
#foreach($orders as $orderkey => $order)
#foreach($days as $day)
{{ Form::hidden($day.'[]', 0, array('id'=> $day.'_hidden_'.$orderkey, 'class' => 'is-checkradio')) }}
{{ Form::checkbox($day.'[]', 1, $order->{$day}, array('id'=> $day.'_'.$orderkey, 'class' => 'is-checkradio')) }}
<label for="<?php echo $day.'_'.$orderkey; ?>"><?php echo $day; ?></label>
#endforeach
#endforeach
OrderController - Update
I've had to use the following in my controller to get the fields to update however whenever a checkbox is left unchecked it will overwrite the next checked value.
$customer = Customer::find($id);
foreach($customer->orders as $key => $order){
$Monday[] = $request->Monday[$key];
};
$updates = array(
'Monday' => $Monday,
);
foreach($updates['orders'] as $k => $update){
$update_order->Monday = $updates['Monday'][$k];
$update_order->save();
};
This part:
foreach($customer->orders as $key => $order){
$Monday[] = $request->Monday[$key];
};
Is creating an array where if the existing data is true and the new data is true, save it. Otherwise delete it. That means any checkboxes that have been deselected will be captured, but any newly checked checkboxes won't be. Is this your intention?
Why not just do:
$updates = array(
'Monday' => $request->Monday,
);
I may not have fully understood your question so please comment and/or amend the Q if you need to clarify further,
Found out how to fix it from Unchecked checkbox returning null value
I needed to add my $orderkey to the name []
So in the end this worked:
#foreach($days as $day)
{{ Form::hidden($day.'['.$orderkey.']', null, array('id'=> $day.'_hidden_'.$orderkey, 'class' => 'is-checkradio')) }}
{{ Form::checkbox($day.'['.$orderkey.']', 1, $order->{$day}, array('id'=> $day.'_'.$orderkey, 'class' => 'is-checkradio')) }}
<label for="<?php echo $day.'_'.$orderkey; ?>"><?php echo $day; ?></label>
#endforeach

Loading Symfony FormBuilder `choices` from doctrine query

I am trying to generate a form element with the below code. The products array that I'm trying to feed in contains all unique values of my Product entity.
My Product and Order entities have a Many-to-Many relationship setup in Doctrine:
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Entity\Product;
class DefaultController extends FOSRestController
{
/**
* #Route("/newOrder", name="new_order")
*/
public function newOrderAction(Request $request)
{
$order = new Order();
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('AppBundle:Product')
->findAllOrderedByName();
$form = $this->CreateFormBuilder($order)
->add('products','choice', array(
'choices' => $products,
'multiple' => true,
))
->add('Save','submit',array('label'=>'Update Order'))
->getForm();
}
}
However, I don't see why the form I'm getting returned shows product#2 for each option as well as generating the <optgroup> label which is making the dropdown menu a bit clunky.
<select id="form_products" name="form[products]">
<optgroup label="0">
<option value="id" >Product#2</option></optgroup>
<optgroup label="1">
<option value="id" >Product#2</option></optgroup>
</select>
The function I have in my product repository is:
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p.description FROM AppBundle:Product p ORDER BY p.description ASC'
)
->getResult();
}
You shouldn't use 'choice' form field but 'entity' one and use 'query_builder' option of entity form field to get products ordered by description.

How to retrieve a value from a query string that stems from a <select> form input?

I simply need to get the selected value from a query string. Currently I just get the index of the list and not the actual value. I cant use Post as I need the value for a pagination search.
I dont get an error but just the number of the index of the list returned. The answer must be simple and I have tried many combinations. I have tried ['Student'] in front of the query as well.
I just couldnt see the answer in the docs and previous posts on this topic didnt work for me either. I am stuck.
The query string has a number {url}students/myindex5?address_suburb=1 //suburb should be a string and it appears as a string in the drop down list
public function myindex5() {
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.address_suburb')
)));
$havefilter = false;
$tsub = $this->request->query['address_suburb'];
// $tsub = array_combine($tsub, $tsub);//didnt work
debug($tsub); //I just get the index value of the list
if (!empty($tsub)) {
// ...
View
echo $this->Form->create('Student', array(
'type' => 'get',
'url' => array('controller' => 'students', 'action' => 'myindex5')
));
echo $this->Form->input('address_suburb', array(
'options' => $filterSuburb,
'empty' => '(choose one)')
);
echo $this->Form->end('Search');
For reference
how to get the selected values from a drop down list in cake php
http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html
When you use a select input, the value send is the value of the option tag:
<select>
<option value="1">Option 1</option>
</select>
If I choose "Option 1", you will get 1, not "Option 1".
If you want to change the value attribute, you need to set something else to the FormHelper::input method options parameter, something like:
array(
'value1' => 'Text 1',
/** etc. **/
);
If you want your value to be ids of Student, just change your find call to:
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.id', 'Student.address_suburb')
)));
If you look at find('list') documentation, you'll see this:
When calling find('list'), the fields passed are used to determine what should be used as the array key and value, and optionally what to group the results by.
So passing Student.id and Student.address_suburb will output a select with Student.id as option value and Student.address_suburb as option text.
If you want something else than Student.id, just change it in the find('list') call, you can even change it to have option like <option value="redcliff">redcliff</option> (same value and text), by doing:
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.address_suburb', 'Student.address_suburb')
)));

cakephp string field used to create checkboxes

I'm using cakephp 2.1 and let me tell you: I just love it.
In my form I have a field that can have multiple answers (checkboxes).
I don't want to create a database field for each option, nor use an HABTM.
Update:
Since I needed several sets of flags, I went the $hasAndBelongsToMany way. (allos to add new flags without fixing the code but editing a table (eg. via phpmyadmin).
1.- Made a table/model for each set of flags I need:
2.- In my main model, declared the relation to each:
var $hasAndBelongsToMany = array('Sample','Format','Report','Openend','Dataproce','Prefield');
3.- In my main Controller, populated an array per table to use:
$openends = $this->Project->Openend->find('list', array(
//'order' => 'name'
));
4.- And use the arrays in the view:
echo $this->Form->input('Dataproce', array('label' => false, 'type' => 'select', 'multiple' => 'checkbox', 'size' => 2));
=== Old question starts here; correct answer worked for only one set of flags ===
I'd like to save a string and use it to to magically create a group of checkboxes that belong to a single data field.
I'm in the middle of it already.
my view:
echo $this->Form->input('pr_reports', array('type' => 'select',
'multiple' => 'checkbox',
'options' => array('0' => 'Interrnal',
'1' => 'Disposition',
'2' => 'Web Disposition',
'3' => 'Marginal',
'4' => 'Custom')))
my controller add method, before saving
// Used serialize() to convert the array to string
$dataString = serialize($this->request->data['Project']['pr_reports']);
$this->request->data['Project']['pr_reports'] = $dataString;
The string is being saved allright (encoded it seems but is ok: a:5:{i:0;s)
My question is how would I go when editing the record in order for the checkboxes to check themselves accordingly? That is, where do I unserialize() the string field and handle this on the edit view?
Is there a better way for this?
Thank you very much for any help.
Carlos GarcĂ­a
==== After solution, troubles to have more than one field with a different set of flags
data is only saved for one field, ignoring the other ====
Hello;
For one field on the table it works just fine as noted;
I'm having a hard time using another field (separate set of flags).
It seems only one behaviour is attached; I was wondering if I should attach them differently.
my fields:
pr_data_format` tinyint(3) unsigned NOT NULL,
pr_report_format` tinyint(3) unsigned NOT NULL,
my controller
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'));
my model
const STATUS_ASCII = 1;
const STATUS_SPSS = 2;
const STATUS_EXCEL = 4;
const STATUS_CUSTOM = 8;
public static function pr_data_formats($value = null) {
$options = array(
self::STATUS_ASCII => __('ASCIId'),
self::STATUS_SPSS => __('SPSSBd'),
self::STATUS_EXCEL => __('Exceld'),
self::STATUS_CUSTOM => __('Customd'),
);
return parent::enum($value, $options);
}
const REP_ASCII = 1;
const REP_SPSS = 2;
const REP_EXCEL = 4;
const REP_CUSTOM = 8;
public static function pr_report_formats($value = null) {
$options = array(
self::REP_ASCII => __('ASCIIr'),
self::REP_SPSS => __('SPSSBr'),
self::REP_EXCEL => __('Excelr'),
self::REP_CUSTOM => __('Customr'),
);
return parent::enum($value, $options);
}
my view
echo $this->Form->input('pr_data_formats', array('options' => Project::pr_data_formats(), 'multiple' => 'checkbox'));
echo $this->Form->input('pr_report_formats', array('options' => Project::pr_report_formats(), 'multiple' => 'checkbox'));
Just can't figure it out, tried:
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'), array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
but no use, only one field is updated.
Can you help? We'll use some 4 or 5 flagsets.
Thank you so much.
Carlos
a proper way to do this is using a behavior. this keeps your model clean and can be applied to several models simply by putting this in your model:
public $actsAs = array('MyBehavior');
now, for serializing I use my Jsonable Behavior:
http://www.dereuromark.de/2011/07/05/introducing-two-cakephp-behaviors/
it basically makes your input array a storable string on save and the string back to an array on read. you could easily adjust this to your needs.
BUT for what you want to do with multiple checkboxes there is even a better thing - bitmasks. I developed a so called Bitmasked behavior - you would need to use 1,2,4,8,... but other than that its the same thing:
http://www.dereuromark.de/2012/02/26/bitmasked-using-bitmasks-in-cakephp/
I use it exactly for the same thing.

Resources