Order items logic with Laravel - laravel

For my Laravel-application I've implemented a sort-functionality. In the list of the options I show two buttons (up and down) which trigger the functions up and down in the OptionController (see below).
Question 1
At the moment I am justing a DECIMAL(30,15) field for the sort-column in the database. I choose this 30,15 randomly. Can you give me an advice, which DECIMAL(?,?) is best for this sort field?
Question 2
I want to move the up and down logic to a place, where I can use it in different controllers with generic models (e.g. Sort::up($models, $item). What would be the right place to place such a logic? Service? Helper-function? ...?
Question 3
When I create a new item (e.g. option in my example below) I need to set the sort automatically to the sort of the last item + 1. Of course, I could do this in the controller when storing it, but can I put this logic to the model itself? And: Where can I put this logic to use it in more than one model without repeating the code?
namespace App\Http\Controllers;
use App\Models\Option;
use App\Models\Attribute;
class OptionController extends Controller
{
public function up($id, $attributeId) {
$options = Attribute::findOrFail($attributeId)->options;
$option = Option::findOrFail($id);
foreach ($options as $index => $o) {
// Search for the current position of the
// option we have to move.
if( $option->id == $o->id ) {
// Will be first element?
if( $index == 1) {
// Set the sort to current first element sort - 1
$option->sort = $options[0]->sort-1;
} else if( $index > 1) {
// Get the previous and the pre-previous items from the options
$pre = $options[$index-1]->sort;
$prepre = $options[$index-2]->sort;
$diff = ($pre - $prepre) / 2;
$option->sort = $prepre + $diff;
}
break;
}
}
$option->save();
Session::flash('message', __(':option moved up.', [ 'option' => $option->name ]));
Session::flash('message-type', 'success');
return redirect()->back();
}
public function down($id, $attributeId) {
$options = Attribute::findOrFail($attributeId)->options;
$option = Option::findOrFail($id);
foreach ($options as $index => $o) {
// Search for the current position of the
// option we have to move.
if( $option->id == $o->id ) {
// Will be last element?
if( $index == count($options)-2 ) {
// Set the sort to current last element sort + 1
$option->sort = $options[count($options)-1]->sort+1;
} else if( $index < count($options)-2) { // ???
// Get the previous and the pre-previous items from the options
$next = $options[$index+1]->sort;
$nextnext = $options[$index+2]->sort;
$diff = ($nextnext - $next) / 2;
$option->sort = $next + $diff;
}
break;
}
}
$option->save();
Session::flash('message', __(':option moved down.', [ 'option' => $option->name ]));
Session::flash('message-type', 'success');
return redirect()->back();
}
}

You can use a trait for this. See link for more details.

Related

How to check the value on the array cart

I have an array cart on codeigniter and creates a function to separate if there is engrave or not on the array like this,
$cart_count = 0;
$cart_cek = $this->cart->contents();
foreach ($cart_cek as $value2) {
if($cart_count >= 1){
break;
}
else{
if($value2['engrave-text'] != ""){
$order_id = $this->generate_order_id_costume();
$cart_count++;
}
else{
$order_id = $this->generate_order_id();
$cart_count++;
}
}
}
but when I run the function it always goes into else with value
$order_id = $this->generate_order_id();
even though there is engrave in the cart array.
For example, I have 3 items in the cart that each have the engrave field. How do I know that there are at least 1 engrave field filled in all the carts?
If you want to check that if the cart contains at least one engrave-text field is filled, you could use array_column to get the engrave-text arrays, then array_filter to check if it's empty :
$cart_cek = $this->cart->contents();
if (!empty(array_filter(array_column($cart_cek, 'engrave-text')))) { // if at least one 'engrave-text' has a value, call generate_order_id_costume() method
$order_id = $this->generate_order_id_costume();
} else { // otherwise call generate_order_id() method
$order_id = $this->generate_order_id();
}
First of all, you don't need to check the count to stop the loop and try using isset().
Try this if you want to check if $this->cart->contents(); has at least 1 engrave-text object.
$cart = $this->cart->contents();
foreach ($cart as $cart_value) {
if(isset($cart_value['engrave-text'])){
$order_id = $this->generate_order_id_costume();
} else {
$order_id = $this->generate_order_id();
}
}

Change the Value of an existing Laravel Collection key

I have a Laravel Collection $page in which I am trying to update some values based on a value in another collection. But instead up updating a new element is being added to $page.
if($page->get('Lang') !== null ){
$l = $page->get('Lang');
foreach ($l as $thisLang => $value) {
if($lang == $thisLang){
// this is where I am attempt is overwrite the variable
$page->Name = $value->Name;
break;
}
}
}
Being fairly new to Laravel I am not sure what I am doing wrong. Any code corrections are appreciated.
I have also tried :
$page->update( ["Name" , $value->Name] );
but get the error
"Method Illuminate\Support\Collection::update does not exist."
Instead of use $page->Name need use $page['Name'].
Corrected code is
if($page->get('Lang') !== null ){
$l = $page->get('Lang');
foreach ($l as $thisLang => $value) {
if($lang == $thisLang){
// this is where I am attempt is overwrite the variable
$page['Name'] = $value->Name;
break;
}
}
}

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)

Advanced Custom Fields—Custom location rule that matches custom taxonomy

I am using the Advanced Custom Fields plugin on a WordPress site. I am aiming to have a custom location rule to show the custom filed group when any term within a custom taxonomy is selected.
Following the custom location rule tutorial on the ACF site I have added custom types, operators, and values to the rules row.
My matching function (code below) works only when the page is reloaded, but not via AJAX. How do I add custom taxonomies to the $options array so the matching function can evaluate via AJAX when custom taxonomy terms are checked/unchecked.
function acf_location_rules_match_taxonomyTerm( $match, $rule, $options ){
// vars
$taxonomies = get_object_taxonomies( $options['post_type'] );
$terms = $options['taxonomy'];
// not AJAX
if( !$options['ajax'] ){
// no terms? Load them from the post_id
if( empty($terms) ){
if( is_array($taxonomies) ){
foreach( $taxonomies as $tax ){
$all_terms = get_the_terms( $options['post_id'], $tax );
if($all_terms){
foreach($all_terms as $all_term){
$terms[] = $all_term->term_id;
}
}
}
}
}
if($rule['operator'] == "<==>"){
$match = false;
if($terms){
$current_terms = get_the_terms($options['post_id'], $rule['value']);
if ( $current_terms && ! is_wp_error( $terms ) ) {
$current_term_ids = array();
foreach ($current_terms as $current_term) {
$current_term_ids[] = $current_term->term_id;
}
}
foreach ($current_term_ids as $current_term_id) {
if( in_array($current_term_id, $terms) ){
$match = true;
}
}
}
}
else{
$match = false;
}
}
return $match;
}
Have you tried this using this plugin? might help. though its a bit late. :)
https://wordpress.org/plugins/advanced-custom-fields-meta-location-rule/

Magento sets null value for non-selected attribute value in admin. It affects the frontend display. How to deal with it?

I've created a new attribute (type: dropdown) that is not a required field.
At this moment, every product shows in the frontend "my attribute: n/a".
After save anything in some product, magento write a null value inside catalog_product_entity_int table for this attribute.
But in the frontend the attribute now appear as "my attribute: No" instead of "N/A".
It looks like a bug, since I didn't touch in the attribute while editing the new product.
Is there a way to deal with it or to apply some rule in my phtml?
Actually this is not a bug. It's a feature.
N/A is displayed when there is no record in the table catalog_product_entity_int for that attribute.
When you add an attribute there are no values for that attribute for any product, but as soon as you save a product that has that attribute, a null value is inserted in the table (as you stated). So no value is different from null value.
All the magic happens here Mage_Catalog_Block_Product_View_Attributes::getAdditionalData().
These are the lines that interest you:
if (!$product->hasData($attribute->getAttributeCode())) { // no value in the database
$value = Mage::helper('catalog')->__('N/A');
} elseif ((string)$value == '') { // empty value in the database
$value = Mage::helper('catalog')->__('No');
}
If you want to change anything override this method.
If you change anything you might want to take a look at Mage_Catalog_Block_Product_Compare_List::getProductAttributeValue().
The same system is used for displaying attribute values in the compare products list.
I've ended up to create 2 observers... One that overrides getValue from Mage_Eav_Model_Entity_Attribute_Frontend_Default and other to override getAdditionalData in Mage_Catalog_Block_Product_View_Attributes as follows:
<?php
class Namespace_Module_Model_Entity_Attribute_Frontend_Default extends Mage_Eav_Model_Entity_Attribute_Frontend_Default{
public function getValue(Varien_Object $object)
{
$value = $object->getData($this->getAttribute()->getAttributeCode());
if (in_array($this->getConfigField('input'), array('select','boolean'))) {
$valueOption = $this->getOption($value);
if (!$valueOption) {
$opt = Mage::getModel('eav/entity_attribute_source_boolean');
$options = $opt->getAllOptions();
if ($options && !is_null($value)) { //added !is_null
foreach ($options as $option) {
if ($option['value'] == $value ) {
$valueOption = $option['label'];
}
}
}
}
$value = $valueOption;
} elseif ($this->getConfigField('input') == 'multiselect') {
$value = $this->getOption($value);
if (is_array($value)) {
$value = implode(', ', $value);
}
}
return $value;
}
}
and
<?php
class Namespace_Module_Block_Product_View_Attributes extends Mage_Catalog_Block_Product_View_Attributes
{
public function getAdditionalData(array $excludeAttr = array())
{
$data = array();
$product = $this->getProduct();
$attributes = $product->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getIsVisibleOnFront() && !in_array($attribute->getAttributeCode(), $excludeAttr)) {
$value = $attribute->getFrontend()->getValue($product);
if (!$product->hasData($attribute->getAttributeCode()) || (string)$value == '') { //modified
$value = Mage::helper('catalog')->__('N/A');
} elseif ($attribute->getFrontendInput() == 'price' && is_string($value)) {
$value = Mage::app()->getStore()->convertPrice($value, true);
}
if (is_string($value) && strlen($value)) {
$data[$attribute->getAttributeCode()] = array(
'label' => $attribute->getStoreLabel(),
'value' => $value,
'code' => $attribute->getAttributeCode()
);
}
}
}
return $data;
}
}

Resources