Comparing laravel collections - laravel

I have two collections: "Instructions" and "Known". Basically I am taking a new set of "Instructions" and checking whether anything is different to what is "Known".
So, the quantity is not massive. I retrieve the info:
$Instructions = Instruction::all();
$Knowns = Known::all();
Now, I'm looking for the differences, and I've tried each of these three methods:
$IssuesFound = $Instructions->diff($Knowns);
$IssuesFound = $Instructions->diffKeys($Knowns);
$IssuesFound = $Instructions->diffAssoc($Knowns);
The thing is, an "Instruction" or "Known" is an item with 17 attributes, and anyone of those attributes can be different. I want to compare the attributes of an "Instruction" with the matching attribute of a "Known". (Both items have the same keys, bot items have a Reference attribute to act as a unique identifier.
What I'm finding is that theese methods give me the item that is different, but doesn't tell me which individual attributes are the mismatch.
foreach ($IssuesFound as $issue)
{
dd($issue);
}
So a method like $IssuesFound = $Instructions->diffKeys($Knowns); will come up with item xxx being different, but I can't see how to find out which attribute of the item it is that is different. Not unless I start nesting loops and iterating through all the attributes - which I'm trying to avoid.
How do I do it?
Thanks in advance. (Laravel 5.6)

Straight from laravel docs, diffAssoc will return what you are asking:
$collection = collect([
'color' => 'orange',
'type' => 'fruit',
'remain' => 6
]);
$diff = $collection->diffAssoc([
'color' => 'yellow',
'type' => 'fruit',
'remain' => 3,
'used' => 6
]);
$diff->all();
// ['color' => 'orange', 'remain' => 6]
You get the attribute from the FIRST collection that is different on the SECOND collection, therefore if you get 3 attributes when calling $diff->all() you will know WHICH attributes ARE DIFFERENT, so you could access them or do whatever you want to, if you post more specific results of what you are getting and what you are trying we can help, but I think you are just not thinking how to use these methods

Related

Difference between [attributes:protected] and [original:protected]

Please could anyone explain to me a difference between [attributes:protected] array and [original:protected] array in laravel when using print_r to an array?
When Model reads data from table, arrays 'original' and 'attribute' contains same data. When you change the attribute value (ex $user->name='John'), the change is reflected only on the 'attributes' array but 'original' remains same. (hence the name).
When update() on a model is called, method checks what has changed comparing two arrays and construct query only for changed fields. Thus, in the case of $users->name change Laravel will not create this code:
UPDATE users set name = 'John', password = 'pass', email = 'email' where id = 1
but this:
UPDATE users set name = 'John' where id = 1
This may not be the only way Eloquent uses 'original' array. I found clockwork helpful when you need to see what's going on under the hood of Eloquent.

Laravel attach pivot to table with multiple values

Background
I'm creating a database revolving around food allergies and I have a many to many relationship between foods and allergies. There is also a pivot value called severity which has a numerical number representing the severity of the allergy for that food item.
This link table looks like this;
food_id|allergy_id|severity
-------|----------|--------
1 | 1 | 3
1 | 4 | 1
2 | 2 | 1
The problem
When trying to update the link table with Eloquent (where $allergy_ids is an array)
$food->allergies()->attach($allergy_ids);
How would I go about adding multiple values to this pivot table at once along with the pivot values?
I can add all the allergy_id's for a particular food item in one go using the above line, but how can I also add in the severity column at the same time with an array of various severity values? Maybe something like
$food->allergies()->attach($allergy_ids, $severity_ids);
Edit: There could be between 0-20 allergies for a specific food item, and a severity rating from 0-4 for each allergy, if this helps at all.
You can.
From this example in Docs (4.2, 5.0):
$user->roles()->sync(array(1 => array('expires' => true)));
Hardcoded version for the first two rows:
$food = Food::find(1);
$food->allergies()->sync([1 => ['severity' => 3], 4 => ['severity' => 1]]);
Dynamically, with your arrays $allergy_ids and $severities in a compatible state (size and sort), you shall prepare your sync data before. Something like:
$sync_data = [];
for($i = 0; $i < count($allergy_ids); $i++))
$sync_data[$allergy_ids[$i]] = ['severity' => $severities[$i]];
$food->allergies()->sync($sync_data);
You can't do it like you' like so I suggest a simple loop:
foreach ($allergy_ids as $key => $id)
{
$food->allergies()->attach($id, array_get($severity_ids, $key));
// should you need a sensible default pass it as a 3rd parameter to the array_get()
}
workaround
However if you wanted to attach multiple allergies with single severity level/id then you could do this:
$food->allergies()->attach($allergy_ids, array('severity' => $singleSeverityValue));
From version 5.1 of Laravel (Currently in Laravel 9.x) onwards it is possible to pass an array as a second argument with all the additional parameters that need to be saved in the intermediate table.
As you can read in the documentation
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$user->roles()->attach($roleId, ['expires' => $expires]);
For convenience, attach and detach also accept arrays of IDs as input:
$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
Then you can simply do
$food->allergies()->attach([1 => ['severity' => 3], 4 => ['severity' => 1]]);
So, on Laravel 9, passing the ids in the array worked for me. Likeso,
$user->roles()->attach([$a->id,$b->id,$c->id]); and so on.
I guess instead of passing the string. We can pass just the id or else convert the string into array.
Easiest indeed is to attach with the extra data, like so:
$retailer->paymentmethods()->attach($paymentmethod, array('currency' => $paymentmethod->currency));
change out the values for food allergy severity, but you get the hint... :-)

Maintain the natural sort of overall results, but sort a subfield array within each result in elasticsearch?

Our defined Type is something like this:
'title' => ... ,
'body' => ... ,
'links' => 'type'=>'object', 'properties'=> array
'link' => ... ,
'locations' => 'type'=>'object', 'properties'=> array
'label' => ... ,
'pin' => ...
)
The records/documents represent businesses that reside in one or more categories, and so the links array will contain all of the potential links to a business, i.e.
[0] => '/Businesses/Hotels/My-Business/',
[1] => '/Businesses/Resorts/My-Business/',
[2] => '/Businesses/Fractional-Ownership/My-Business/'
So when we run a query on the terms Business Resort this listing is included in the result set. At the moment though, we don't know which link would be most appropriate to display on the results page, so we just default to the first, in this case the one with ../Hotels/.. in the path.
Is it possible to order the links according to their own score/relevancy within the search so that the link order on the returned result would instead be:
[0] => '/Businesses/Resorts/My-Business/',
[1] => '/Businesses/Hotels/My-Business/',
[2] => '/Businesses/Fractional-Ownership/My-Business/'
The order of the links should not have any influence on the order of natural results from the overall query.
EDIT : The second use case which I've added above is, we also store locations for each business, and would like to order the location list for each resulting business by their proximity to a set of coordinates. We know how to order the entire result set by _geo_distance but need to know how to do it on a specific field, and like above, without affecting the overall result order.
Script based sorting will give you exactly what you want to sort on. But track_scores seems it will do what you're looking for, sorting doesn't use scores by default.
And for ordering geo distance sorting by a specific field you could do so client side, providing the specific field value of the selected field? I'm not sure this answers your second question.

Laravel: object or other structures (array, json..) to the view?

There are several ways you may pass data to a Laravel Blade view.
In this savvy discussion Laravel hidden attributes. e.g. Password - security Antonio Carlos Ribeiro states (and i agree) that:
"you are not supposed to send objects to a view. In the MVC pattern, views should receive data that are relative to them, processed data, not objects, because they don't have to know anything about your business logic."
I am learning Laravel and everywhere i look i often see examples like:
$users = User::all();
return View::make('users')->with('users', $users);
This one specially comes from the official documentation.
What method should be ideally used?
Do you transform your objects in array or other formats prior to send them to the view?
Do you selectively clean your data from all the unnecessary values prior of pass it to the template engine?
Apart being probably academically wrong, what are the potential risks for passing the object to the view?
If you use following approach
$users = User::all();
return View::make('users')->with('users', $users);
You will get a collection of User objects in your model and can use a loop to print out all the User objects and it's fine, what risk could be doing this, it's upon you, so you should know what should do but if you don't want to pass a collection object then it's also possible to pass only an array of arrays using:
$users = User::all()->toArray();
return View::make('users')->with('users', $users);
So, you'll get an array of arrays in the view where each child array will contain a perticular user's details. The array may look something like this:
array (size=2)
0 =>
array (size=5)
'id' => int 1
'username' => string 'heera' (length=5)
'email' => string 'heerasheikh#ymail.com' (length=21)
'created_at' => string '2014-01-20 06:10:53' (length=19)
'updated_at' => string '2014-01-23 10:23:50' (length=19)
1 =>
array (size=5)
'id' => int 2
'username' => string 'usman' (length=5)
'email' => string 'mdusyl#yahoo.com' (length=16)
'created_at' => string '2014-01-20 06:10:53' (length=19)
'updated_at' => string '2014-01-20 09:06:23' (length=19)
But, you can use the Laravel's traditional way and there is no risk at all. Don't follow something blindly, use your sense and ask yourself, what risk it may rise for you. You are only about to loop the collection, nothing else. Now, the choice is your's, if you pass the collection then you can use object notation, i.e. $user->username but if you pass an array then you have to use something like $user['username'], that's it.

Custom Magento Report for Taxable/Non-Taxable sales

Let me preface by saying I'm new to Magento as well as Data Collections in general (only recently begun working with OOP/frameworks).
I've followed the excellent tutorial here and I'm familiar with Alan Storm's overviews on the subject. My aim is to create a custom Magento report which, given a start/end date, will return the following totals:
Taxable Net (SUM subtotal for orders with tax)
Non-Taxable Net (SUM subtotal for orders without tax)
*Total Gross Sales (Grand total)
*Total Net Sales (Grand subtotal)
*Total Shipping
*Total Tax
*For these figures, I realize they are available in existing separate reports or can be manually calculated from them, however the purpose of this report is to give our store owner a single page to visit and file to export to send to his accountant for tax purposes.
I have the basic report structure already in place in Adminhtml including the date range, and I'm confident I can include additional filters if needed for order status/etc. Now I just need to pull the correct Data collection and figure out how to retrieve the relevant data.
My trouble is I can't make heads or tails of how the orders data is stored, what Joins are necessary (if any), how to manipulate the data once I have it, or how they interface with the Grid I've set up. The existing tutorials on the subject that I've found are all specifically dealing with product reports, as opposed to the aggregate sales data I need.
Many thanks in advance if anyone can point me in the right direction to a resource that can help me understand how to work with Magento sales data, or offer any other insight.
I have been working on something extremely similar and I used that tutorial as my base.
Expanding Orders Join Inner
Most of the order information you need is located in sales_flat_order with relates to $this->getTable('sales/order')
This actually already exists in her code but the array is empty so you need to populate it with the fields you want, here for example is mine:
->joinInner(
array('order' => $this->getTable('sales/order')),
implode(' AND ', $orderJoinCondition),
array(
'order_id' => 'order.entity_id',
'store_id' => 'order.store_id',
'currency_code' => 'order.order_currency_code',
'state' => 'order.state',
'status' => 'order.status',
'shipping_amount' => 'order.shipping_amount',
'shipping_tax_amount' => 'order.shipping_tax_amount',
'shipping_incl_tax' => 'base_shipping_incl_tax',
'subtotal' => 'order.subtotal',
'subtotal_incl_tax' => 'order.subtotal_incl_tax',
'total_item_count' => 'order.total_item_count',
'created_at' => 'order.created_at',
'updated_at' => 'order.updated_at'
))
To find the fields just desc sales_flat_order in mysql.
Adding additional Join Left
Ok so if you want information from other tables you need to add an ->joinLeft() for example I needed the shipment tracking number:
Create the Join condition:
$shipmentJoinCondition = array(
$orderTableAliasName . '.entity_id = shipment.order_id'
);
Perform the join left:
->joinLeft(
array('shipment' => $this->getTable('sales/shipment_track')),
implode(' AND ', $shipmentJoinCondition),
array(
'track_number' => 'shipment.track_number'
)
)
Sorry I couldn't go into more depth just dropping the snippet for you here.
Performing Calculations
To modify the data returned to the grid you have to change addItem(Varien_Object $item) in your model, basically whatever is returned from here get put in the grid, and well I am not 100% sure how it works and it seems a bit magical to me.
Ok first things first $item is an object, whatever you do to this object will stay with the object (sorry terrible explanation): Example, I wanted to return each order on a separate line and for each have (1/3, 2/3, 3/3), any changes I made would happen globally to the order object so they would all show (3/3). So keep this in mind, if funky stuff starts happening use PHP Clone.
$item_array = clone $item;
So now onto your logic, you can add any key you want to the array and it will be accessible in Grid.php
For example(bad since subtotal_incl_tax exists) :
$item_array['my_taxable_net_calc'] = $item['sub_total'] + $item['tax'];
Then at the end do:
$this->_items[] = $item_array;
return $this->_items;
You can also add more rows based on the existing by just adding more data to $this->_items[];
$this->_items[] = $item_array;
$this->_items[] = $item_array;
return $this->_items;
Would return same item on two lines.
Sorry I have started to lose the plot, if something doesn't make sense just ask, hope this helped.
Oh and to add to Block/Adminhtml/namespace/Grid.php
$this->addColumn('my_taxable_net_calc', array(
'header' => Mage::helper('report')->__('Taxable Net'),
'sortable' => false,
'filter' => false,
'index' => 'my_taxable_net_calc'
));

Resources