I need to make sortable select2_multiple with Laravel Backpack - or a custom one with similar functionality.
In the docs there is a way to simply implement select2_multiple just like this:
$this->crud->addField([
'label' => "Ingredients",
'type' => 'select2_multiple',
'name' => 'ingredients', // the method that defines the relationship in your Model
'entity' => 'ingredients', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'model' => Ingredient::class, // foreign key model
'pivot' => true,
'select_all' => true
]);
But! I dont see any way to store sort data of the entries we selected!
I have created field SORT in my DB ingredients_to_products
table, this should do the trick, but how to implement this to Backpack?
DB structure is really simple: products, ingredients, ingredients_to_products tables. Ingredients_to_products table have only 3 fields: product_id, ingredient_id, sort.
Laravel 5.6, Backpack CRUD 3.3
Any help is appreciated. Thanks!
I found how to do this.
I made a custom view and put some changes to CrudController, manually resolving sort value.
In view section I created some JS and new fields that represents sort values of relevant entries.
By the way, there is a $entry->pivot function that also can help.
So, what i've done: created new view called "ingredients"
<table class="table table-bordered" id="table">
<p>Connected ingredients:</p>
<select hidden multiple name="ingredients[]" id="ingredients">
<?php foreach ($field['value'] as $item){ ?>
<option selected data-id="<?= $item->id ?>" value="<?= $item->id ?>"><?= $item->name ?></option>
<?php } ?>
</select>
<select hidden multiple name="ingredients_sort[]" id="ingredients_sort">
<?php foreach ($field['value'] as $item){ ?>
<option data-id="<?= $item->id ?>" selected value="<?= $item->pivot->sort ?>"><?= $item->pivot->sort ?></option>
<?php } ?>
</select>
<?php foreach ($field['value'] as $item){ ?>
<tr class="item">
<td>
<p><?= $item->name ?></p>
</td>
<td>
<input class="sorter form-control" data-id="<?= $item->id ?>" value="<?= $item->pivot->sort ?>">
</td>
<td>
<span data-id="<?= $item->id ?>" class="btn btn-primary remover">Remove</span>
</td>
</tr>
<?php } ?>
So as you see we now have the ingredients and ingredients_sort arrays that syncronized by positions by JavaScript.
We get list of all avaliable ingredients by AJAX.
Sorting value - is a connected input field to every ingredient entry.
let selected_ingredients = <?= isset($field['value']) ? json_encode($field['value']) : '' ?>;
let ingredients = [];
document.addEventListener("DOMContentLoaded", load);
function load(){
getIngredients();
}
function getIngredients() {
$.get('/ingredients/get').done( (response) => {
ingredients = JSON.parse(response);
printList(ingredients);
} );
}
function printList(ingredients){
// #todo : print search
for (let i = 0; i < ingredients.length; i++){
let item = ingredients[i];
let _item = document.createElement('div');
_item.className = 'item btn btn-default';
_item.setAttribute('data-id', item.id);
_item.innerHTML = `${item.name}`;
_item.onclick = function () {
// #todo: add ingredient to product
let needAdd = true;
for (let i = 0; i < selected_ingredients.length; i++){
if (selected_ingredients[i].id == item.id){
needAdd = false;
}
}
if (needAdd){
$('#ingredients').append(`<option selected data-id="${item.id}" value="${item.id}">${item.name}</option>`);
$('#ingredients_sort').append(`<option data-id=${item.id} selected value="0">0</option>`);
$('#table').append(
`<tr><td>
<p>${item.name}</p>
</td>
<td>
<input class="sorter form-control" data-id="${item.id}" value="0">
</td>
<td>
<span data-id="${item.id}" class="remover btn btn-primary">remove</span>
</td>
</tr>`);
$('.remover').unbind().click(function () {
$(this).parent().parent().remove();
});
$('.sorter').unbind().change(function(){
let id = $(this).attr('data-id');
$(`#ingredients_sort [data-id=${id}]`).val( $(this).val() ).html( $(this).val() );
});
}
};
$('#list').append(_item);
}
$('.remover').unbind().click(function () {
let id = $(this).attr('data-id');
$(`#ingredients [data-id=${id}], #ingredients_sort [data-id=${id}]`).remove();
$(this).parent().parent().remove();
});
$('.sorter').unbind().change(function() {
let id = $(this).attr('data-id');
$(`#ingredients_sort [data-id=${id}]`).val( $(this).val() ).html( $(this).val() );
});
}
function remove(){
console.log(this);
}
And the last step - putting the Update method in ItemCrudController (which in my app is "product" controller)
public function update(UpdateRequest $request)
{
// your additional operations before save here
$redirect_location = parent::updateCrud($request);
$obj = \App\Models\Item::find( $request->get('id') );
$obj->ingredients()->detach();
$i = 0;
foreach ( $request->get('ingredients') as $item){
$obj->ingredients()->save( \App\Models\Ingredient::find($item), ['sort' => $request->get('ingredients_sort')[$i]]);
$i++;
}
return $redirect_location;
}
So - it works.
The result photo below. Hope this will help someone.
Related
i'm using laravel in my project , so when i add a product in the shop cart all the data is displayed except the product image.
This is the cartcontroller.php:
public function add(Request $request) {
$produit=productmodel::find($request->id);
Cart::add(array(
'id' =>$request->id, // inique row ID
'name' =>$request->product_name,
'price' => $request->product_price,
'quantity' =>$request->product_quantity,
'attributes' => array('photo'=>$request->product_image)));
return redirect ('shop-cart');
}
and this is the shop-cart.blade.php
<tbody>
#foreach(\Cart::getContent() as $item)
<tr>
<td class="cart__product__item">
<div class="cart__product__item__title">
<img src="{{asset('storage/product/September2020/'.$item->attributes['photo'])}}" alt="">
<h6> {{Str::words($item->name,20) }}</h6>
#foreach($item->attributes as $key => $value)
<dl class="dlist-inline small">
<dt>{{ ucwords($key) }}: </dt>
<dd>{{ ucwords($value) }}</dd>
</dl>
#endforeach
</div>
</td>
<td class="cart__price"> {{$item->price}} TND</td>
<td class="cart__quantity">
{{ $item->quantity }}
</td>
<td class="cart__total"> {{ $item->price * $item->quantity }} TND</td>
<td class="cart__close"><i class="fa fa-times"></i>
</td>
</td>
</tr>
#endforeach
</tbody>
</table>
</div>
#endif
If you are using darryldecode/cart for cart. You can go to your vendor folder and make some slight changes to add method of Cart.php file.
public function add($id, $name = null, $price = null, $quantity = null, $image = null, $attributes = array(), $conditions = array(), $associatedModel = null)
{
// if the first argument is an array,
// we will need to call add again
if (is_array($id)) {
// the first argument is an array, now we will need to check if it is a multi dimensional
// array, if so, we will iterate through each item and call add again
if (Helpers::isMultiArray($id)) {
foreach ($id as $item) {
$this->add(
$item['id'],
$item['name'],
$item['price'],
$item['quantity'],
$item['image'],
Helpers::issetAndHasValueOrAssignDefault($item['attributes'], array()),
Helpers::issetAndHasValueOrAssignDefault($item['conditions'], array()),
Helpers::issetAndHasValueOrAssignDefault($item['associatedModel'], null)
);
}
} else {
$this->add(
$id['id'],
$id['name'],
$id['price'],
$id['quantity'],
$id['image'],
Helpers::issetAndHasValueOrAssignDefault($id['attributes'], array()),
Helpers::issetAndHasValueOrAssignDefault($id['conditions'], array()),
Helpers::issetAndHasValueOrAssignDefault($id['associatedModel'], null)
);
}
return $this;
}
$data = array(
'id' => $id,
'name' => $name,
'price' => Helpers::normalizePrice($price),
'quantity' => $quantity,
'image'=>$image,
'attributes' => new ItemAttributeCollection($attributes),
'conditions' => $conditions
);
if (isset($associatedModel) && $associatedModel != '') {
$data['associatedModel'] = $associatedModel;
}
// validate data
$item = $this->validate($data);
// get the cart
$cart = $this->getContent();
// if the item is already in the cart we will just update it
if ($cart->has($id)) {
$this->update($id, $item);
} else {
$this->addRow($id, $item);
}
$this->currentItemId = $id;
return $this;
}
Now you can simply store image in cart as below
$userId = auth()->user()->id;
\Cart::session($userId)->add(array(
'id' => $request->id,
'name' =>$request->item_name,
'price' =>$request->item_price,
'quantity' => $request->quantity,
'image'=>$request->image,
'attributes' => array(),
));
And view your stored image from path like
#foreach(Cart::session(auth()->user()->id)->getContent() as $items)
<div class="row pt-5">
<div class="col-md-3 offset-md-2">
<img class="card-img-top" src="{{asset('photos').'/'.$items->image}}"
style="height:120px; width:120px;"alt="Card image cap">
</div>
<div class="col-md-6 ">
<h5 class="font-weight-bold">{{$items->name}}</h5>
Rate: Rs {{$items->price}}<br>
Qty: {{$items->quantity}}<br>
<?php
$price="";
$price=$items->quantity*$items->price;
?>
Price: Rs {{$price}}<br>
<button class="btn-sm btn-outline-danger"><i class="far fa-trash-alt"></i></button>
</div>
</div>
<hr>
#endforeach
This is the structure of my table "task"
projectname
employee
clientname
task
Dependencies are as follows
One project has multiple task
One project has multiple employees
I need to create a dropdown list when user selects a particular project tasks relevant to them will automatically load to the next dropdown list. In this situation I do not need primary and foriegn key relationship. Any help would be really appreciated
This is my controller
public function Task(){
$data['cname'] = $this->welcome4->show_students3();
$data['projects'] = $this->welcome4->show_students();
$data['employee'] = $this->welcome4->show_students2();
$this->load->view('template/navigation');
$this->load->view('template/sideNav');
$this->load->view('template/header');
$this->load->view('Task',$data);
$this->load->view('template/footer');
}
This is my model
function show_students2(){
$query = $this->db->get('employee');
$query_result = $query->result();
return $query_result;
}
function show_students3(){
$query = $this->db->get('clientdetails');
$query_result = $query->result();
return $query_result;
}
function show_students4(){
$query = $this->db->get('task');
$query_result = $query->result();
return $query_result;
}
This is my view
<div class="form-group">
<label>Select Project</label>
</div>
<div class="form-group">
<select name="projectname" class="input form-control">
<option value="none" selected="selected">Select Project</option>
<?php foreach($projects as $s):?>
<option value="<?php echo $s->projectname?>"><?php echo $s->projectname?></option>
<?php endforeach;?>
</select>
</div>
<div class="form-group">
<label>Select Client</label>
<select name="cname" class="input form-control">
<option value="none" selected="selected">Select client</option>
<?php foreach($cname as $s):?>
<option value="<?php echo $s->cname?>"><?php echo $s->cname?></option>
<?php endforeach;?>
</select>
</div>
<div class="form-group">
<label>Select Employee</label>
</div>
<div class="form-group">
<select name="employee" class="input form-control">
<option value="none" selected="selected">Select Employee</option>
<?php foreach($employee as $s):?>
<option value="<?php echo $s->employee?>"><?php echo $s->employee?></option>
<?php endforeach;?>
</select>
</div>
This loads all projects, clients, employee in the database.But now I want when project is selected in the first drodown, second dropdown should show only relevant clients and employees to it. Not all of them
Lets consider this will be your Projects select box, in which you are loading data dynamically on page load with php.
<select name="projectname" id="projectname"></select>
Tasks select box.
<select name="tasks" id="tasks"></select>
Inside your controller add below mentioned code. This function will be used for get data from ajax call.
public function getTasks() {
$Id = $this->input->post('project_id');
if ($Id):
$this->load->model('Task_model', 'Tasks', TRUE);
$data = $this->Tasks->getProjectTasks($Id);
if ($data):
$result['status'] = true;
$result['records'] = $data;
endif;
else:
$result['status'] = false;
endif;
echo json_encode($fdata);
}
Inside model please add this function. Which will fetch data from DB
ans pass it to controller back.
public function getProjectTasks($id = null) {
if ($id):
$this->db->select('id,task');
$this->db->where('project_id', $id);
$this->db->where('status', TRUE);
$query = $this->db->get('tasks');
$records = $query->result();
if ($records):
return $records;
else:
return false;
endif;
else:
return false;
endif;
}
And finally in you View file please add below function.
$(document).on('#projectname', 'change', function() {
var projectId = $(this).val();
$.ajax({
type: 'POST',
url: 'URL',
data: {project_id: projectId},
dataType: "json",
beforeSend: function() {
$('#tasks')
.empty()
.append('<option selected="selected">Select Task</option>');
},
success: function(data) {
if (data.status) {
$.each(data.records, function(i, item) {
$('#tasks').append($('<option>', {
value: item.value,
text: item.text
}
));
});
} else {
$('#tasks')
.empty()
.append('<option selected="selected">No Task available</option>');
}
}
});
});
i hope all of you can help me to solve my application problem
my problem is when i would like to show data from my multiple checkbox , data success to show but just showing my first data that i have check.
example is i check data 1 , data 2 , data 3, but the only data 1 are showing on my page.
my controller :
function comparison()
{
if ($this->input->post('submit')) {
foreach ($id_product = $this->input->post('id_product') as $rm) {
$show_compare = $this->Compare->start_compare($rm);
}
$data['comparison'] = $show_compare;
$data['title'] = "Comparison";
$data['meta_keywords'] = ". . .";
$data['meta_descriptions'] = ". . .";
$this->load->view('theme/comparison',$data);
}
}
My Model :
function start_compare($id_product)
{
$this->db->select('product.id_subcategory,product.type,product.product_name,specificcategory.specificcategory_name,specification_biostar.*');
$this->db->join('specification_biostar', 'specification_biostar.id_product = product.id_product', 'left');
$this->db->join('specificcategory', 'specificcategory.id_specificcategory = product.id_subcategory', 'left');
$this->db->where('product.id_product', $id_product);
$sql = $this->db->get('product')->result_array();
return $sql;
}
my view (option multiple-checkbox) :
<div class="box-body">
<input type="checkbox" name="id_product[]" id="txt" onClick="EnableSubmit3(this)" value="<?php echo $row['id_product']; ?>"><label>Choose</label>
</div>
my view (result data) :
<table class="table">
<?php foreach ($comparison as $row){ ?>
<tr>
<td colspan="2"><?php echo $row['id_product'] ?></td>
</tr>
<?php } ?>
</table>
in your code take $show_compare = array(); before foreach loop and use array_push in foreach.
$show_compare = array();
foreach ($id_product = $this->input->post('id_product') as $rm) {
array_push($show_compare,$this->Compare->start_compare($rm));
}
$data['comparison'] = $show_compare;
I'm working on a php online shop website using codeigniter framework and i want to be able to filter my table results using checkboxes with ajax
view:
<input type="checkbox" name="brand" value="acer">
<input type="checkbox" name="brand" value="lenovo">
<input type="checkbox" name="pret" value="1000">
<table>
<tbody>
<?php foreach ($laptops_toate as $laptops_all) { ?>
<tr>
<td><img src="http://localhost:82/ci/images/emag/<?php echo $laptops_all->file ?>"></td>
<td><p>Laptop <?php echo $laptops_all->brand ?> </p>
</td>
</tr>
<?php } ?>
</tbody>
</table>
Controller:
public function laptops()
{
$filter = array(
'pret' => $this->input->get('pret'),
'brand' =>$this->input->get('brand')
);
$data['laptops_toate'] = $this->emag_model->laptops_toate_grid($filter);
$this->renders('emag/laptops', $data);
}
model:
public function laptops_toate_grid($filter = null){
$this->db->select('*')
->from('laptop_notebook');
// $query = $this->db->get('laptop_notebook')->result();
// return $query;
if($filter['brand']){
$this->db->where('brand', $filter['brand']);
}
if($filter['pret']){
$this->db->where('pret', $filter['pret']);
}
$query = $this->db->get()->result();
return $query;
}
The problem is now at the ajax code, i don't know how to send the data filter to the server in order to receive the success function.
View:
<script>
$("input[checkbox]").change(function(){
$.ajax({
url: route,
dataType: 'json',
success: function(data){
$.each(data, function(index, element) {
$("tbody").empty();
$("tbody").append("<tr><td>"+
"Laptop "+element.brand+""+
"</td></tr>");
});
}
});
Controller:
public function laptops()
{
$filter = array(
'pret' => $this->input->get('pret'),
'brand' =>$this->input->get('brand')
);
echo json_encode($this->emag_model->laptops_toate_grid($filter));
}
Now just do console.log(data); first inside the $.each() to see what your array looks like.
I'm defining a form in the actions, and passing it to the template. It's failing to set the default values when saving. Can you tell me what I'm doing wrong?
In modules\question\actions:
public function executeNew(sfWebRequest $request)
{
$this->form = new questionForm();
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
// create question
$user = $this->getUser()->getGuardUser()->getId();
$question = new Question();
$question->setQuestionTitle($this->getRequestParameter('question_title'));
$question->setQuestionBody($this->getRequestParameter('question_body'));
$question->setUserId($user);
$question->save();
$question->addTagsForUser($this->getRequestParameter('tag'), $user);
return $this->redirect('question/index');
}
//set current user as default
$this->form->setDefault('user_id',$this->getUser()->getGuardUser()->getId());
}
In \modules\question\templates:
<?php echo $form->renderFormTag('question/new') ?>
<table>
<?php echo $form['question_title']->renderRow(array('size' => 40), 'Question title:') ?>
<?php echo $form['question_body']->renderRow(array('cols' => 40, 'rows' => 10), 'Description:') ?>
<?php echo input_auto_complete_tag('tag', '', '#tag_autocomplete', 'autocomplete=off', 'use_style=true') ?>
<tr>
<td colspan="2">
<input type="submit" value="ask it" />
</td>
</tr>
</table>
</form>
One of two options:
Put the setDefault code in the QuestionForm class
OR
Put the setDefault code right after your instantiate the form object
The common practice is option #1, as it results in loose coupling between your form configuration and the actions.