I'm working on a symfony project (using Doctrine as well) and I'd like to implement a staggered search on one of the pages, per example:
User searches for an author in the first select2 box (which is pulling data from DB via Ajax), and once an item is selected, there is a second select2 box called title, which I would like to display only titles belonging to the selected author.
Here's the controller side code (both Ajax and controller) for the initial box. Any ideas how I could construct the query for the second select2?
The part that related to the initial select2 that queries the DB for the results and autosuggested items:
public function searchAjaxAuthorAction()
{
$em = $this->getDoctrine()->getManager();
$term = $this->get('request')->query->get('term');
$limit = $this->get('request')->query->get('page_limit', 1);
$rep = $em->getRepository('StephenPersianArtBundle:Main');
if($term){
$entities = $rep->createQueryBuilder('m')
->where('m.orig_author LIKE ?1')
->orderBy('m.orig_author', 'ASC')
->setParameter('1','%'.$term.'%')
->getQuery();
}else{
$entities = $rep->createQueryBuilder('m')
->groupBy('m.orig_author')
->getQuery();
}
$entities = $entities->execute();
$resultset = array();
foreach($entities as $entity){
if($entity->getOrigAuthor()){
$resultset[] = array(
//'id' => $entity->getId(),
'id' => $entity->getOrigAuthor(),
'text' => $entity->getOrigAuthor()
);
}
}
$return = json_encode($resultset);//jscon encode the array
return new Response($return,200,array('Content-Type'=>'application/json'));
}
There's also another part related to this that basically loads data into a table based on the item selected in the select2, but I don't consider this relevant for the issue I'm having as all this is happening before the final query is completed.
Any help will be much appreciated!
Related
I'm trying to achieve some like an Ecommerce website where a Product has Many Categories.
I have this form named PackageItem table (let's compare it to PRODUCT) and when I click the Item it gets the Items name from the Item table (let's also compare it to CATEGORIES)
I have this q-select that accepts multiple items.
This is my return in Vue
createOptions: [],
my Methods for getting the Items from the backend and bind it to the q-select
allPackageItemsData() {
const bearerToken = localStorage.getItem("Bearer");
this.$axios
.get("http://localhost:8000/api/allPackageItems", {
headers: {
Authorization: `Bearer ${bearerToken}`,
},
})
.then((response) => {
this.allPackageItems = response.data;
this.createOptions = response.data.map(
(packageitem) =>
// "(Item) " +
packageitem.name
// + " & " +
// "(Item ID) " +
// packageitem.id
);
});
},
As you can see I'm mapping so that I can get the Item name and bind it to the q-select (See Package Item Form above.)
However, I need to display the names in the Frontend and upon selecting the Items name in the q-select I wanted to get the ID of it and save it to the database. In short I need to display the name as it is but upon selecting it I need to get the ID and pass it to the database
The reason for this is that I need to get the ID of the Items so that I can display it
I'm getting a controller like this one
$packageitems = DB::table('packageitems')
->select(
'items.name',
'packageitems.price',
)
->join('items', 'items.id', '=', 'packageitems.item_id')
->where('items.activeInactive', '=', 'Active')
->get();
I wanted to save a Package Item that has Many Items as many as I want and upon getting the Item names I wanted to create a price for it. Let's say it as a promo in a grocery you have 5 different chips and save the price for 2 dollar as a promo more like a LAZADA PACKAGE or SHOPEE PACKAGE.
I'm trying to achieve like this one.
This is my Create PackageItem Controller
$
user = Auth::user();
$item = Item::arrayPackageItemSelect();
$fields = $request->validate([
'user_id' => 'required',
'item_id' => 'required',
'price' => 'required|numeric'
]);
$package = PackageItem::create([
'user_id' => $user->id,
'item_id' => implode(',' , $fields['item_id']), // If i put implode it gives
//error
'price'=> $fields['price']
]);
return response()->json($package, 201);
I tried using implode as I seen it one of the tutorials however it is using one table only that in my case I'm doing this with a join from another table.
my package Item model.
my Item model.
so I created a function arrayPackageItemSelect that gets the Id of the Items but display it to the frontend by the name. I got this from this article https://www.artofcse.com/learning/product-view-insert-update-delete
However, I'm having a hard time figuring out this logic both the backend and frontend. I tried searching an I can't find anything I'm looking for. I wanted to make a CRUD out of this.
first create the package with the price and user_id
then
check the request if the request->item is an array
if true $items = collect($request->items); collect the request->item array
then
$items->each(function ($item) use ($package ) {
$package ->items()->create($item);
});
this 3 lines of code will do the logic in inserting the package items referenced to the package id
on the package model
protected $fillable = [user_id,price];
public function items() {hasMany(item::class)}
item model
public function package(){belognsTo(packageItem::class)}
I created a custom filter to filter products on category pages and in search.
Filter itself seems to be correct (two products are visible from origin twelve) but pagination is wrong (total size is still 12 and link to 2nd page is rendered) (see 1st screenshot: ).
This filter is also not taken into account when list of other filters is rendered (see 2nd screeshot: there is still 12 products calculate instead of 2).
Filter itself is rather simple:
public function apply(\Magento\Framework\App\RequestInterface $request)
{
$filter = $request->getParam($this->getRequestVar());
if (!$filter) {
return $this;
}
$collection = $this->getLayer()->getProductCollection();
$customCollection = $this->collectionFactory->create();
$customCollection->addFieldToFilter('value', $filter);
$customIds = $customCollection->getColumnValues('entity_id');
$collection->addAttributeToFilter([['attribute' => 'available_custom_ids',['in' => $customIds]],['attribute' =>'available_custom_ids',['null'=>true]]], null, 'left');
$this->getLayer()->getState()->addFilter($this->_createItem($filter, $filter));
return $this;
}
I injected this filter to the \Magento\Catalog\Model\Layer\FilterList with:
public function aroundGetFilters(
\Magento\Catalog\Model\Layer\FilterList $subject,
\Closure $proceed,
\Magento\Catalog\Model\Layer $layer
) {
$result = $proceed($layer);
$result[] = $this->_objectManager->create(
\Vendor\Module\Model\Layer\Filter\ExampleFilter::class,
['layer' => $layer]
);
return $result;
}
What I'm doing wrong? It seems Magento is using two different collections on the same page, but I'm totally lost which is which (and why).
Side note: I used custom filter instead of simple make "available_custom_ids" filterable because I wanted to include also products with available_custom_ids == NULL to the results. Is it correct approach?
I'm trying to create a Joomla (3.x) component and struggling with using subforms. There doesn't seem to be much documentation for using subforms besides e.g. https://docs.joomla.org/Subform_form_field_type
For my component I have one parent table and some associated database rows from a child table.
The idea is to display an edit form for that parent table using Joomla's XML syntax for forms and in that edit form also display a subform with multiple items (the associated rows from the child table).
I would like to be able to modify the parent table fields but also in one go the associated child table rows (of course one could just edit each row associated to the parent table individually but I'm guessing that would be a terrible user experience). Or am I approaching this thing the wrong way?
Now, I know how to implement/show a subform and also know how to show the parent table fields and populate those fields with the right data. But how do I populate or refer to the subform using the parent form?
I have this function inside my component model (which inherits from JModelAdmin).
protected function loadFormData()
{
$data = JFactory::getApplication()->getUserState('com_mycomp.edit.parent.data', array());
if (empty($data))
{
$data = $this->getItem();
// how to refer to subform fields inside $data?
}
return $data;
}
I know if a field is called name or title I can just change the $data object after $this->getItem(), e.g. $this->set('name', 'John Doe').
Let's say the field of type subform has a name attribute of books and I wanted to insert one or more rows, how would I refer to it? I've tried dot syntax in various forms, e.g.: $data->set('books.1.childfield') or $data->set('books.pages1.childfield'). But it doesn't seem to refer to the right form.
There is of course getForm function in the same model file, however I do not think a subform should be loaded independently of the containing parent form?
public function getForm($data = array(), $loadData = true)
{
$app = JFactory::getApplication();
$form = $this->loadForm('com_mycomp.parent', 'parent', array('control' => 'jform', 'load_data' => $loadData));
if (empty($form))
{
return false;
}
return $form;
}
EDIT:
Already answered my own question.
Never mind. I figured it out after taking a break for some time and trying again (inspecting the form inputs again and taking a deep breath).
This is the format used:
$data->set('nameofsubformfield',
[
'nameofsubformfield0' => [
'fieldwithinsubform' => 'value-of-field-within-subform'
]
]);
This seems to work! I'm using this within getItem function now. Just have to loop and put loop counter in place of the zero after nameofsubformfield. See code below for some context (function resides in parent model).
public function getItem($pk = null)
{
$data = parent::getItem((int)$pk);
if (empty($data))
{
return false;
}
$childModel = JModelLegacy::getInstance('child', 'MycompModel');
$rowChildren = $childModel->getChildrenByParentID((int)$data->get('id'));
$childArray = [];
for ($i = 0; $i < count($rowChildren); $i++)
{
$childArray['children'. $i] = [
'name' => $rowChildren[$i]['name']
];
}
$data->set('children', $childArray);
return $data;
}
I am trying to edit the Orders Grid so that I can export data to XML with the quantity of each item sold on each line.
Now I can only access the total amount of the order, which is not sufficient. I would like to build a Grid with the information available for each order in Order View > Information > Ordered items.
Is this possible with a few lines of code ?
Here is what I did for now :
I tried to manually add columns in the _prepareColumns() function from Grid.php.
Basically, I tried to add a quantity column :
$this->addColumn('total_qty_ordered', array(
'header' => Mage::helper('sales')->__('Qty'),
'index' => 'total_qty_ordered',
'filter_index' => 'sales_flat_order.total_qty_ordered',
));
However I do not get any total quantity, and of course I do not get the product split in each order. I do not really know where to look to implement this product split.
Thanks by advance.
EDIT :
Here is what is I get thanks to the extension
Order grid
However, I cannot export this product split because the last column is a kind of 'embedded' of other information. So I get an empty column in XML.
I don't usually recommend extensions -- I believe most of them can be a bit more trouble than they are worth, but in this case, I do recommend this one:
http://www.magentocommerce.com/magento-connect/enhanced-admin-grids-editor.html
It's free -- code is fairly clean and you can add that by clicking the grid customization button on the orders page, clicking more options, then finding the items drop down and adding those columns in. Will show you a table of all of the items.
If you want to post the code you've written the custom way, I can help you customize that to do the job too.
We published a whole blog post on how to add any data to your order grid. Hope this will help you! https://grafzahl-io.blogspot.de/2016/11/how-to-display-m2e-order-data-or.html
So the solution would be to copy the Sales Grid Block to a local module and add your column like this example:
$this->addColumn('order_type', array(
'header' => Mage::helper('sales')->__('Order Type'),
'width' => '100px',
'align' => 'left',
'index' => 'order_type',
'renderer' => 'yourmodule/adminhtml_sales_grid_renderer_m2eAttribute',
'filter_condition_callback' => array($this, '_filterM2eConditionCallback')
));
The filter_condition_callback is a method in the Grid Block. The renderer is another class as you can see in the namespace. In the renderer you can define what is displayed in your column. The filter_condition_callback defines how the grid should act, in case someone will filter by your custom column.
It will look like this:
/**
* filter callback to find the order_type
* of orders through m2e (amazon, ebay, ...)
*
* #param object $collection
* #param object $column
* #return Yourname_Yourmodule_Block_Adminhtml_Sales_Order_Grid
*/
public function _filterM2eConditionCallback($collection, $column) {
if (!$value = $column->getFilter()->getValue()) {
return $this;
}
if (!empty($value) && strtolower($value) != 'magento') {
$this->getCollection()->getSelect()
// join to the m2mepro order table and select component_mode
->join(
'm2epro_order',
'main_table.entity_id=m2epro_order.magento_order_id',
array('component_mode')
)
->where(
'm2epro_order.component_mode = "' . strtolower($value) . '"');
} elseif(strtolower($value) == 'magento') {
$this->getCollection()->getSelect()
->join(
'm2epro_order',
'main_table.entity_id=m2epro_order.magento_order_id',
array('component_mode')
)
->where(
'm2epro_order.component_mode = NULL');
}
return $this;
}
As you can see, there are two joins to collect the data we need to filter for.
This is how the renderer looks like which will display the data in the grid:
class Yourname_Yourmodule_Block_Adminhtml_Sales_Grid_Renderer_M2eAttribute
extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{
public function render(Varien_Object $row)
{
// do whatever you need, to display your data
// get the id of the row order data
$orderId = $row->getEntityId();
// get the related m2e order data
$orders = Mage::getModel('M2ePro/Order')
->getCollection()
->addFieldToSelect('component_mode')
->addFieldToFilter('magento_order_id', $orderId);
if($orders) {
$data = $orders->getFirstItem()->getData();
if(isset($data['component_mode'])) {
return ucfirst($data['component_mode']);
}
}
// return the string "magento" if there is no m2e relation
return 'Magento';
}
}
The link will show you other example, how to show data in the order grid.
I had a problem sorting a paginated list when using a calculated field such as COUNT() in cakephp 1.3
Let's say that i have two models: Article and Comments (1 article x N comments) and i want to display a paginated list of the articles including the number of comments for each one. I'd have something like this:
Controller:
$this->paginate = array('limit'=>80,
'recursive'=>-1,
'fields'=>array("Article.*","COUNT(Comment.id) as nbr_comments"),
'joins'=>array(array( 'table' => 'comments',
'alias' => 'Comment',
'type' => 'LEFT',
'conditions' => array('Comment.article_id = Article.id'))
),
'group'=>"Article.id"
);
(i had to overwrite the findCount() method in order to paginate using group by)
The problem is that in the view, the sort() method won't work:
<th><?php echo $this->Paginator->sort('nbr_comments');?></th> //life is not that easy
I was able to create a workaround by "cheating" the pagination and sort:
Controller
$order = "Article.title";
$direction = "asc";
if(isset($this->passedArgs['sort']) && $this->passedArgs['sort']=="nbr_comments")
$order = $this->passedArgs['sort'];
$direction = $this->passedArgs['direction'];
unset($this->passedArgs['sort']);
unset($this->passedArgs['direction']);
}
$this->paginate = array(... 'order'=>$order." ".$direction, ...);
$this->set('articles', $this->paginate());
if($order == "clicks"){
$this->passedArgs['sort'] = $order;
$this->passedArgs['direction'] = $direction;
}
View
<?php $direction = (isset($this->passedArgs['direction']) && isset($this->passedArgs['sort']) && $this->passedArgs['sort'] == "nbr_comments" && $this->passedArgs['direction'] == "desc")?"asc":"desc";?>
<th><?php echo $this->Paginator->sort('Hits','clicks',array('direction'=>$direction));?></th>
And it works.. but it seems that is too much code for something that should be transparent to the developper. (It feels like i'm doing cake's work) So i'm asking if there's another simpler way. Maybe cake has this functionallity but decided to hide it.. o_O.. there's nothing about this on the documentation, and i haven't found another good solution on S.O... how do you do it?
Thanks in advance!
maybe your problem can be solved using Virtual fields ?
If you create a custom field for your computed field, you will be able to sort using Paginator->sort() method on that custom field.
I found that solution there in the comments (there is no need to customize the paginate method etc. using custom fields instead of in adnan's initial solution).
What you may want to do is use Cake's counterCache functionality in your model definition. Rather than calculating the comment counts at read time, you add nbr_comments as an int field in your articles table. Every time a Comment is inserted or deleted, nbr_comments will be updated automatically.
In your Comment model:
var $belongsTo = array(
'Article' => array(
'counterCache' => 'nbr_comments'
)
);
Now you can just use $this->Paginator->sort('Article.nbr_comments'); You wont need to do anything funky in your controller.
My solution for this problem was the following:
put your calculate field as a virtual field
$this->Comment->virtualFields = array(
'nbr_comments' => 'COUNT(Comment.id)'
);
then, you can use that field, in your paginate order variable
$order = array("Comment.nbr_comments" => "DESC");
$this->Paginator->settings = array("order" => $order);