How to include a list view in the details show view sonata bundle - view

I'm trying to have a query result in the show view and then to normally edit/delete the rows as in the list view (pagination as well).
As far I did this:
protected function configureShowFields(ShowMapper $show): void
{
$userId = $this->getSubject()->getId();
$show
->add('lastName')
->add('firstName')
->add('phoneNumber')
->add('email')
->add('fullAddress')
->end();
$show
->with('Cars', [
'class' => 'col-md-8',
'box_class' => 'box box-solid',
])
->add('cars', null, [
'query_builder' => function (EntityRepository $er) use ($userId) {
return $er->createQueryBuilder('u')
->where('u.user = :user')
->setParameter('param', $userId);
},
'template' => 'Admin/list_items.html.twig'
])
->end();
}
but in the template I've only added a table and displayed the car items but I must be able to click on the item and go to their detail page. Any ideas what to use to achieve that?

Related

how to check if cart has already added not allow more and update if different cart

I'm using composer require gloudemans/shoppingcart I am not sure how to maintain amount.
When i'm using one route that says add item when i'm using this route adding multiple item
How can i conditionally setup to add item in cart if this is unique
public function bookItem($id) {
$item = Item::where([
'status' => '1',
'id' => $id
])->first();
$product = Cart::add($item->id, $item->name, 1, $item->price); // This should not call always if it has not generated a row id then it should call
Cart::update($product->rowId, ['price' => 200]); // Will update the price if it is differ
return redirect()->route('book.item', ['id' => $item->id]);
}
I am not sure how to manage it. please guide
Looks like the package has a getContents() function that gathers all items into a collection. It also has a search(Closure) function that calls getContents() and then uses Laravel's filter function on the collection and returns the result.
$search = Cart::search(function($key,$value) use ($item) {
return $value === $item->id;
})->first();
if(!empty($search)){
Cart::update($search->rowId, ['price' => 200]);
}
else {
$product = Cart::add($item->id, $item->name, 1, $item->price);
}
Definitely check out the Laravel collection docs if you aren't familiar. This is the entey on filters:
https://laravel.com/docs/5.8/collections#method-filter

gridview check box multi delete in yii2

i simply want to have a gridview that has checkbox column in front of each row and my admins can delete row by check all or check one or check how many box they disire and then by click on delete button all checked row remove from their view
in the back its important to update their delete column to 1 a row not delete
here is my code for delete one id
controller(ignore persian words)
public function actionDelete($id=[])
{
$model = \Yii::$app->db->createCommand()
->update('tbl_post',['Delete'=>1],['id'=>$id])->execute();
if($model==true){
\Yii::$app->session->setFlash('با موفقیت نیست شد');
}else{
\Yii::$app->session->setFlash('با موفقیت نیست نشد');
}
return $this->redirect('index');
}
here is view(ignore persian words)
//in baraye ine ke form taiid shod payam bede
foreach (Yii::$app->session->getAllFlashes() as $key => $message) {
echo '<div class="alert alert-' . $message . '">' . $key . '</div>';
}
//-------------------------table it self
echo \yii\grid\GridView::widget([
'dataProvider' => $adp,
'caption' => 'لیست تمامی محتوا ها',
'captionOptions' => ['id' => 'atro-caption'],
'headerRowOptions' => ['class' => 'atro-th'],
'columns' => [
['class' => \yii\grid\SerialColumn::className(),],
['class' => \yii\grid\CheckboxColumn::className(),
'checkboxOptions' => function ($a) {
return ['value' => $a->id];
}],
'Title',
'FK_PostType',
'FK_Author',
]
]);
Before I suggest you anything about your problem you should know the basic rules of programming that never use reserved keywords for function naming variable or database field names, you have used the Delete as a column name you should change it immediately to something like is_deleted. i will be using this name in my example reference.
About your problem, you have 2 ways to do it.
Add a button and bind javascript click event to it which will serialize all the selected checkboxes and then use an ajax post request to submit the selected ids and mark them delete.
Wrap your Gridview inside a form tag and then use a submit button to submit that for to the delete action.
I will demonstrate the first option to you
add the button on top of your GridView
<?=Html::button('Delete', ['class' => 'btn btn-danger', 'id' => 'delete'])?>
Then assign an ID to the pjax container
<?php Pjax::begin(['id' => 'my-grid']);?>
Paste this javascript on top of your view but update the url of the ajax call to your actual controller/delete action
$this->registerJs('
$(document).on("click","#delete",function(e){
let selected=$(".grid-view>table>tbody :input");
let data=selected.serialize();
if(selected.length){
let confirmDelete = confirm("Are you sure you want to delete?");
if(confirmDelete){
$.ajax({
url:"/test/delete",
data:data,
dataType:"json",
method:"post",
success:function(data){
if(data.success){
$.pjax({container:"#my-grid"});
}else{
alert(data.msg);
}
},
error:function(erorr,responseText,code){}
});
}
}else{
alert("select someitems to delete");
}
});
', \yii\web\view::POS_READY);
And change your delete action to the following, try using transaction block so that if something happens in the middle of the operation it will revert all the changes back, change the model name and namespace to the appropriate model, I assumed your model name is Post.
public function actionDelete()
{
if (Yii::$app->request->isPost) {
$selection = Yii::$app->request->post('selection');
$response['success'] = false;
$transaction = Yii::$app->db->beginTransaction();
try {
\frontend\models\Post::updateAll(['is_deleted' => 1], ['IN', 'id', $selection]);
$response['success'] = true;
$transaction->commit();
} catch (\Exception $ex) {
$transaction->rollBack();
$response['msg'] = $ex->getMessage();
}
echo \yii\helpers\Json::encode($response);
}
}

Conditional Validation in Yii2

I have a radio button with two values (required field) based on that value one field is shown (there are two fields which are intially hidden, its shown based on value of radio button) which should be required. So I used conditional validation for the initially hidden fields.
This is my model code:
public function rules()
{
return [
[['receipt_no', 'date_of_payment', 'payment_method_id',
'total_amount'], 'required'],
['nonmember_name', 'required', 'whenClient' => function($model)
{
return $model->is_member == 2;
}, 'enableClientValidation' => false],
['member_id', 'required', 'whenClient' => function($model)
{
return $model->is_member == 1;
}, 'enableClientValidation' => false],
[['receipt_no', 'date_of_payment', 'payment_method_id',
'total_amount','is_member'], 'required','on' => 'receipt'],
];
}
I use a scenario receipt, is_member is the radio button field. If I select a value of 1 for is_member then field member_id is visible and it should be a required one. If is_member has a value 2 then nonmember_name is displayed and it should become a required field. With my code in model I managed to achieve it. But now other actions (saving a new row of data into model) using this model is having error
Array ( [nonmember_name] => Array ( [0] => Name cannot be blank. ) )
So my question is how can I make conditional validation specific to a scenario (I think that my error is due to required rule defined in conditional validation )
EDIT:
This is my radio button
<?= $form->field($model, 'is_member')->radioList(array('1'=>'Member',2=>'Non Member'))->label('Member or Not'); ?>
In rules
public function rules()
{
return [
[
'nonmember_name',
'required',
'when' => function ($model) {
return $model->is_member == 2;
},
'whenClient' => "function (attribute, value) {
return $('#id').val() == '2';
}"
]
];
}
I prefer to use functions inside the model's rules, which makes it a lot easier to work with in the future.
One thing to mention that a lot of answers don't mention, is that you MUST manually re-trigger the Yii2 client side validation!
$("#w0").yiiActiveForm("validateAttribute", "createuserform-trainer_id");
In my example below, there are 2 types of accounts: trainer and trainee. In my admin panel, the admin can create a new user. If they choose "trainer" there is nothing more to do. If they choose a "trainee", that "trainee" must be assigned a "trainer" to go under.
So in code terms:
If user role == trainee, trainer_id is required and show it's form input.
Else hide the trainer_id input and trainer_id will not be required.
Model rules:
public function rules()
{
return [
[
'trainer_id', 'required', 'when' => function ($model) {
return $model->role == 2;
}, 'whenClient' => "isUserTypeTraineeChosen"
],
];
}
View after your form:
<?php $this->registerJs('
function isUserTypeTraineeChosen (attribute, value) {
if ($("#createuserform-role").val() == 2) {
$(".field-createuserform-trainer_id").show();
} else {
$("#createuserform-trainer_id").val(null);
$(".field-createuserform-trainer_id").hide();
}
return ($("#createuserform-role").val() == 2) ? true : false;
};
jQuery( "#createuserform-role" ).change(function() {
$("#w0").yiiActiveForm("validateAttribute", "createuserform-trainer_id");
});
jQuery( "#createuserform-role" ).keyup(function() {
$("#w0").yiiActiveForm("validateAttribute", "createuserform-trainer_id");
});
'); ?>
Note: This is a dropdown list, so change and keyup both are necessary to accurately detect it's change statuses. If your not using a dropdown, then both may not be necessary.
I also have targeted the trainer_id input to be hidden by default using CSS, as the default user type is a trainer.

How can I pass an object to the sheet method in Laravel Excel?

I'm trying to pass an object into the Laravel Excel sheet method but I'm not sure how. I get an undefined variable error - which I realize is because I'm inside a function that doesn't have scope to reach the object.
Ultimately I'm loading up regtypes from one table - attendees from another and creating a sheet for each regtype that contains all attendees of that type.
My code:
public function export()
{
$date = date('Y_m_d');
\Excel::create('CMO_Connect_Attendees_Export_'.$date, function($excel) {
$regtypes = Regtype::all();
foreach ($regtypes as $regtype) {
if( $regtype->attendees(3)->count() ) {
$excel->sheet('Attendees', function($sheet) {
$date = date('Y_m_d');
$attendees = new Attendee;
$atts = Attendee::where('block_id', '=', \Input::get('block_id'))
->where('regtype_id', '=', $regtype->id)
->get();
$sheet->setStyle(array(
'font' => array(
'name' => 'Arial',
'size' => 12,
'bold' => false
)
));
$sheet->loadView('attendees.export', array('atts' => $atts))->with('curdate',$date)->with('regtype_name',$regtype->name);
});
} //endif
} //endforeach
$excel->export('xls');
});
}
As I was writing this up I figured it out, pretty simple I should have known as it's simply a method of a Laravel Facade, so you pass the object as an array like so:
$excel->sheet('Attendees', array('regtype' => $regtype), function($sheet) {

How to add an autocomplete field in a Symfony2 form for a collection and using Propel?

I'm using Symfony 2.1 forms with PropelBundle and I'm trying to refactor a form that had a drop-down list of objects (to select from) to instead use a jquery autocomplete field (working with AJAX). For the dropdown list I was using the following code (which worked perfectly for the drop-down) in my form type:
$builder->add('books', 'collection', array(
'type' => 'model',
'options' => array(
'class' => 'MyVendor\MyBundle\Model\Book',
'property' => 'title',
),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
));
For the sake of giving a little context, let's say we are creating a new "Reader" object and that we would like to select the Reader's favorite books from a list of available "Book" objects. A collection type is used so that many "favorite books" can be selected in the new "Reader" form. Now, I would like to change the above to use autocomplete. For doing so, I tried to implement a Data Transformer to be able to get a Book object from a simple text field that could be used for the Autocomplete function to pass the Book ID as indicated in the answer to this Question. However, I was not able to figure out how to make the Data Transformer work with a collection type and Propel classes. I created a BookToIdTransformer class as indicated in the Symfony Cookbook and tried the following in the "ReaderType" file:
$transformer = new BookToIdTransformer();
$builder->add(
$builder->create('books', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
))->addModelTransformer($transformer)
);
With the above, I get a "Call to undefined method: getId" exception (apparently the Transformer expects a PropelCollection of Books, not a single Book object..). Does anyone know how to go about it? or let me know if there are other ways to implement the autocomplete in Symfony using Propel and allowing for selecting multiple objects (e.g. a collection of books)?
The solution I ultimately went for is slightly different from my previous answer. I ended up using a "text" field type instead of a "collection" field type in my "ReaderType" form, since I ended up using the Loopj Tokeninput jQuery plugin which allows for selecting multiple objects (e.g. "Favorite Book") to associate to my main object (e.g. "Reader" object) in the form. Considering that, I created a "Data Transformer" for transforming the objects' ids passed (in a comma separated list in the text field) into a Propel Object Collection. The code is shared as follows, including a sample ajax object controller.
The key part of the "ReaderType" form looks as follows:
$transformer = new BooksToIdsTransformer();
$builder->add(
$builder->create('books', 'text', array(
'required' => false,
))->addModelTransformer($transformer)
);
The "Data Transformer" file looks like this:
// src/MyVendor/MyBundle/Form/DataTransformer/BooksToIdsTransformer.php
namespace MyVendor\MyBundle\Form\DataTransformer;
use \PropelObjectCollection;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use MyVendor\MyBundle\Model\BookQuery;
class BooksToIdsTransformer implements DataTransformerInterface
{
public function transform($books)
{
if (null === $books) {
return "";
}
if (!$books instanceof PropelObjectCollection) {
throw new UnexpectedTypeException($books, '\PropelObjectCollection');
}
$idsArray = array();
foreach ($books as $book) {
$idsArray[] = $book->getId();
}
$ids = implode(",", $idsArray);
return $ids;
}
public function reverseTransform($ids)
{
$books = new PropelObjectCollection();
if ('' === $ids || null === $ids) {
return $books;
}
if (!is_string($ids)) {
throw new UnexpectedTypeException($ids, 'string');
}
$idsArray = explode(",", $ids);
$idsArray = array_filter ($idsArray, 'is_numeric');
foreach ($idsArray as $id) {
$books->append(BookQuery::create()->findOneById($id));
}
return $books;
}
}
The ajax controller that returns a json collection of "books" to the Tokeninput autocomplete function is as follows:
namespace MyVendor\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use MyVendor\MyBundle\Model\BookQuery;
class ClassAjaxController extends Controller
{
public function bookAction(Request $request)
{
$value = $request->get('q');
$books = BookQuery::create()->findByName('%'.$value.'%');
$json = array();
foreach ($books as $book) {
$json[] = array(
'id' => $book->getId(),
'name' => $book->getName()
);
}
$response = new Response();
$response->setContent(json_encode($json));
return $response;
}
}
And finally, the router in the "routing.yml" file:
ajax_book:
pattern: /ajax_book
defaults: { _controller: MySiteBundle:ClassAjax:book }

Resources