pass an index to the method reduce() laravel - laravel

I have this function created to show a piechart but I need each element to show a different color. For this I have created an array with all the colors and I need in each iteration of the reduce() method to have an index to access the colors[i]. I have tried this way and it does not work. Any suggestion?
$i = 0;
$pieChartModel = $options->groupBy('survey_options_id')
->reduce(function (PieChartModel $pieChartModel, $data) use ($i) {
$type = $data->first()->survey_options_id;
$value = $data->sum('value');
// $color = "#" . substr(md5(rand()), 0, 6);
$NameOption = Survey_options::where('id', $type)->pluck('name');
return $pieChartModel->addSlice($NameOption, $value, $this->colors[$i]->hexa);
$i++;
}, (new PieChartModel())->setAnimated($this->firstRun)->setDataLabelsEnabled(true));

A problem I'm seeing in your code is how you're incrementing $i AFTER a return statement.
After taking a look at the source code for the reduce() function
/**
* Reduce the collection to a single value.
*
* #param callable $callback
* #param mixed $initial
* #return mixed
*/
public function reduce(callable $callback, $initial = null)
{
$result = $initial;
foreach ($this as $key => $value) {
$result = $callback($result, $value, $key);
}
return $result;
}
You should be able to use the key (or index) in the callback.
->reduce(function ($carry, $item, $key) { ... }, $initial)
->reduce(function (PieChartModel $pieChartModel, $data, $i) {
...
}, (new PieChartModel())->setAnimated($this->firstRun)->setDataLabelsEnabled(true))

Related

Custom filter API Platform not working

I am trying to implement a custom or-filter in API Platform. But for some reason it is not loading. Find below my configuration.
This is my filter:
<?php
namespace AppBundle\Filter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Common\Annotations\AnnotationReader;
final class SearchFilter extends AbstractFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
if ($property === 'search') {
$this->logger->info('Search for: ' . $value);
} else {
return;
}
$reader = new AnnotationReader();
$annotation = $reader->getClassAnnotation(new \ReflectionClass(new $resourceClass), \AppBundle\Filter\SearchAnnotation::class);
if (!$annotation) {
throw new \HttpInvalidParamException('No Search implemented.');
}
$parameterName = $queryNameGenerator->generateParameterName($property);
$search = [];
$mappedJoins = [];
foreach ($annotation->fields as $field)
{
$joins = explode(".", $field);
for ($lastAlias = 'o', $i = 0, $num = count($joins); $i < $num; $i++) {
$currentAlias = $joins[$i];
if ($i === $num - 1) {
$search[] = "LOWER({$lastAlias}.{$currentAlias}) LIKE LOWER(:{$parameterName})";
} else {
$join = "{$lastAlias}.{$currentAlias}";
if (false === array_search($join, $mappedJoins)) {
$queryBuilder->leftJoin($join, $currentAlias);
$mappedJoins[] = $join;
}
}
$lastAlias = $currentAlias;
}
}
$queryBuilder->andWhere(implode(' OR ', $search));
$queryBuilder->setParameter($parameterName, '%' . $value . '%');
}
/**
* #param string $resourceClass
* #return array
*/
public function getDescription(string $resourceClass): array
{
$reader = new AnnotationReader();
$annotation = $reader->getClassAnnotation(new \ReflectionClass(new $resourceClass), \AppBundle\Filter\SearchAnnotation::class);
$description['search'] = [
'property' => 'search',
'type' => 'string',
'required' => false,
'swagger' => ['description' => 'Filter on ' . implode(', ', $annotation->fields)],
];
return $description;
}
}
In api_filters.yml:
driver.custom_search_filter:
class: 'AppBundle\Filter\SearchFilter'
autowire: true
tags: [ { name: 'api_platform.filter' } ]
In my annotation file:
<?php
namespace AppBundle\Filter;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Annotation\Target;
use Doctrine\Common\Annotations\AnnotationException;
/**
* #Annotation
* #Target("CLASS")
*/
final class SearchAnnotation
{
public $fields = [];
/**
* Constructor.
*
* #param array $data Key-value for properties to be defined in this class.
* #throws AnnotationException
*/
public function __construct(array $data)
{
if (!isset($data['value']) || !is_array($data['value'])) {
throw new AnnotationException('Options must be a array of strings.');
}
foreach ($data['value'] as $key => $value) {
if (is_string($value)) {
$this->fields[] = $value;
} else {
throw new AnnotationException('Options must be a array of strings.');
}
}
}
}
And finally in my entity:
/**
* A driver that bring meals from hub to customer.
*
*
* #ApiResource(
* attributes={
* "filters"={"driver.search_filter","driver.custom_search_filter"},
* "denormalization_context"={"groups"={"post_driver"}}
* }
* )
* #Searchable({"firstName"})
*
* #ORM\Entity
* #ORM\Table(name="vendor_driver")
*/
class Driver
{
It is exactly as according to the issue that was reported here:
https://github.com/api-platform/core/issues/398
I am not getting any errors, but the filter is simply not working. I am seeing it in Swagger. But when I enter a value in Swagger, the db returns all entities. Its never reaching the filterProperty method.
Does anyone have an idea?
I've just managed to get this working by removing the autowiring, e.g.
my.custom_search_filter:
class: AppBundle\Filter\CustomSearchFilter
arguments:
- '#doctrine'
- '#request_stack'
- '#logger'
tags: [ { name: 'api_platform.filter', id: 'search' } ]
Hope that helps.
Did you import your custom filter in the Driver entity? In Regexp example of custom filter and here they are explicitly import their custom filters like:
use AppBundle\Filter\SearchAnnotation as Searchable;
/**
* #Searchable({"name", "description", "whatever"})
*/
class Product
{

How to use pagination in collection fetched by sortBy and where operators?

I need pagination for my collection in which I have to apply lots of filters. But I'm unable to achieve this. I'm new to laravel so please show me the right way.
My controller:
public function index(Request $request)
{
$view = $request['view'] ? $request['view'] :'grid';
$purpose = $request['purpose'] ? $request['purpose'] : 'rent';
$sort = $request['sort'] ? $request['sort'] : 'asc';
$properties = $properties->where('purpose' , $purpose);
if($sort == 'asc');
$properties = $properties->sortBy('price');
else
$properties = $properties->sortByDesc('price');
$properties = $properties->paginate(5);
return view('frontend.properties.index', [ 'view'=>$view , 'properties' => $properties , 'request'=> $request->all() ]);
}
I have had this issue and in order to solve that, I have created a trait called PaginateCollection:
/*
* Paginate the Laravel Collection before and/or after filtering.
*
*/
trait PaginateCollection
{
/**
* Paginate the collection.
*
* #param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection $collection
* #param integer $perPage
* #param integer $currentPage
* #return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($collection, $perPage = 10, $currentPage = 1)
{
$offSet = ($currentPage * $perPage) - $perPage;
$otherParams = [
'path' => request()->url(),
'query' => request()->query()
];
return new LengthAwarePaginator(
$collection->forPage(Paginator::resolveCurrentPage() , $perPage),
$collection->count(),
$perPage,
Paginator::resolveCurrentPage(),
$otherParams
);
}
}
Perhaps, this should help you out.

Getting the next object in a collection in Laravel

I have mutliple Chapters that belong to a Module.
On a chapter page I want to check if I am on the last one in the module, but I'm getting a bit stuck.
// inside Chapter model.
// The $module var is a made by something like Module::with('chapters')->find(1);
public function getNext($module){
// Convert to array so we can call some of
// the array functions to navigate the array
$chapters = $module->chapters->keyBy('id')->toArray();
// get the last element in the array
end($chapters);
// If the last element's key is the same as this one,
// there is no "next" link
if(key($chapters) == $this->id){
return false;
}
// So there must be a next link. First, reset internal array pointer
reset($chapters);
// Advance it to the current item
while (key($chapters) !== $this->id) next($chapters);
// Go one further, returning the next item in the array
next($chapters);
// current() is now the next chapter
return current($chapters);
}
Cool! So this lets me know if there is a next chapter and even returns it as an array with all of its data. But I'm getting into massive problems. The Chapter has a few other methods on it which I can't call on the 'next' element as its an array, not an object any more.
// Chapter.php
public function url(){
return url('chapter/' . $this->id);
}
$module = Module::with('chapters')->find(1);
$chapter = Chapter::find(1);
$next = $chapter->getNext($module);
if( $next )
echo $next->url();
This gives me (obviously)
Call to a member function url() on array
So I need to rewrite this function, but I can't work out how to get the next object in a Laravel collection.
public function getNext($module){
$last = $module->chapters->last();
// If the last element's key is the same as this one,
// there is no "next" link
if($last->id == $this->id){
return false;
}
....
How can I traverse the collection to get the next Chapter as an object?
After a little bit I have worked out my own solution:
public function getNext($module){
$last = $module->chapters->last();
// If the last element's key is the same as this one,
// there is no "next" link
if($last->id == $this->id){
return false;
}
$current_order = $this->order;
$filtered = $module->chapters->filter(function ($item) use ($current_order) {
return $item->order > $current_order;
});
return $filtered->first();
}
Open to any other neater ways of doing it though! Thanks
You can create collection macros as:
Collection::macro('previous', function ($key, $value = null) {
if (func_num_args() == 1) $value = $key; $key = 'id';
return $this->get($this->searchAfterValues($key, $value) - 1);
});
Collection::macro('next', function ($key, $value = null) {
if (func_num_args() == 1) $value = $key; $key = 'id';
return $this->get($this->searchAfterValues($key, $value) + 1);
});
Collection::macro('searchAfterValues', function ($key, $value) {
return $this->values()->search(function ($item, $k) use ($key, $value) {
return data_get($item, $key) == $value;
});
});
Then you can use it as:
$module = Module::with('chapters')->find(1);
$chapter = Chapter::find(1);
$next = $module->chapters->next($chapter->id)
// or
$next = $module->chapters->next('id', $chapter->id)

Laravel - #each get current iteration

I am using the #each blade directive to loop through an array of query results and render them (in this case, the #each directive tends to be a bit of a better idea than a foreach loop since the HTML that will be renders changes depending on the result of the query). This all works fine, but I have the issue of trying to get the current index of the query itself.
In other words, using the #each directive, I need to be able to access the key (or index) of the current loop in the partial that is being rendered.
If you have anything that you need to know, just ask.
Thank you for reading!
Looking at the source code that's run for the #each directive yields the following:
Illuminate\View\Factory.php
/**
* Get the rendered contents of a partial from a loop.
*
* #param string $view
* #param array $data
* #param string $iterator
* #param string $empty
* #return string
*/
public function renderEach($view, $data, $iterator, $empty = 'raw|')
{
$result = '';
// If is actually data in the array, we will loop through the data and append
// an instance of the partial view to the final result HTML passing in the
// iterated value of this data array, allowing the views to access them.
if (count($data) > 0) {
foreach ($data as $key => $value) {
$data = ['key' => $key, $iterator => $value];
$result .= $this->make($view, $data)->render();
}
}
// If there is no data in the array, we will render the contents of the empty
// view. Alternatively, the "empty view" could be a raw string that begins
// with "raw|" for convenience and to let this know that it is a string.
else {
if (Str::startsWith($empty, 'raw|')) {
$result = substr($empty, 4);
} else {
$result = $this->make($empty)->render();
}
}
return $result;
}
If you notice on these lines:
$data = ['key' => $key, $iterator => $value];
$result .= $this->make($view, $data)->render();
A variable called $key is automatically passed to the rendered view. That should contain the current index of your array loop.

get the values of custom options

I'm trying to alter a price based on some custom options set. Therefore I'm trying to get the value a customer has entered, not the default values set in the backend. To do this I'm using the event catalog_product_get_final_price used in Mage_Bundle_Model_Product_Price. I have registered the following observer:
public function observer_callback($evt_obs)
{
$event = $evt_obs->getEvent();
$data = $event->getData();
/* #var $collection Mage_Catalog_Model_Resource_Product_Collection */
$collection = $data['collection'];
$items = $collection->getItems();
/* #var $item Mage_Catalog_Model_Product */
foreach ($items as $item) {
if ( $item->getName() == 'Bundel Test2') {
$options = $item->getCustomOptions();
/* #var $option Mage_Catalog_Model_Product_Option */
foreach ($options as $option) {
// Here I'm trying to get the value given by the user/customer
var_dump($option->getData());
}
}
}
return $this;
}
It is a custom option from a bundle type. So the product can't be configurable.
I'm new to magento so I'm probably missing something.
Can anyone help me?
Hope this piece of code can help you:
public function productFinalPrice($observer){
$product = $observer->getEvent()->getProduct();
$productType=$product->getTypeID();
if($productType == 'your_product_type')
{
$option = $product->getCustomOptions();
$searchedOption = null;
//search for your option;
foreach ($product->getOptions() as $o) {
if($o->getTitle()=="your_attribute_title" && $o->getType()=="your_type_of_option(eg. area"){
$optionId = $o->getOptionId();//got your searched optionId
break;
}
}
foreach($option as $key => $o) {
if($key == "option_".$optionId) {
$searchedOption = $o;
//here you get the option object with the values in it
}
}
$articleNumber = $searchedOption->getData('value'); // getthe value of your option
//calculate final price like you need it
$product->setFinalPrice($finalPrice);
}
return $this;
}
best regards

Resources