I am working with a system where inventory is stored in a single database table. I need help to find the best way to add/remove items from this.
It is stored as follows;
3:1|8:2|5:3|4:4
the first number represents quantity and the second number is the item ID. The Pipe | splits these items.
So 3:1 = quantity 3 of item 1.
I am trying to use PHP to look at this string, find if the item is there. If it is add to or remove from it. OR if the item isn't there then create it.
I know I would have to use an array to achieve this but I'm a little lost at how this would be done, any help with this would be greatly appreciated.
Thanks.
I would make a small class that holds an item -> quantity map (stored as an array) that is created from that string, and builds the string back when requested.
This makes use of:
explode to split the string parts,
implode combined with array_map to join them back.
Code:
class DataStructure
{
private $data = [];
public function __construct(string $data)
{
foreach (explode('|', $data) as $item_quantity) {
list($quantity, $item) = explode(':', $item_quantity);
$this->data[$item] = (int)$quantity;
}
}
public function getItemQuantity(int $item): ?int
{
return $this->data[$item] ?? null;
}
public function setItemQuantity(int $item, int $quantity)
{
$this->data[$item] = $quantity;
}
public function __toString(): string
{
return implode('|', array_map(function ($item) {
return $this->data[$item] . ':' . $item;
}, array_keys($this->data)));
}
}
Note that this doesn't include error handling, for the purpose of this example.
Demo: https://3v4l.org/pqRQh
Demo (PHP 5.6 compatible): https://3v4l.org/tK9Q9
Related
I have a situation in which there is an ID which I receive in a function. This ID when I pass to an API it provides me a relation list, for example if I pass as an ID A, I get:
From
To
Type
A
B
RelationType1
A
C
RelationType2
C
D
RelationType3
D
A
RelationType4
Now I need to find in a recursive manner the relations for all unique ID I get (A,B,C,D) and for each I need to list down the IDs with types until I cannot find any more relations .
Finally I need to save all this data within the database which is not an issue but it will be a combination of From,To and Type.
This is being attempted in php in which I am working on using a class as the basis to start off, thus implementing DFS to do . Is there a better alternative with efficient calls to the API and faster processing.
Thanks for your help.
simple recursive. something like this
basic
class Relations {
public static function getLinksFromDB(relation_id){
return $db->array(); // return an array of matches based on the passed in $relation_id from the database, using your normal query here.
}
public static function getLinks(relation_id){
$ret = [];
$r_ids = Relations::getLinksFromDB(r_id); // when this returns nothing, you will have reached the end of your links, with an exception, if you have any amount which is self contained like A->B, B->C and C->A, then you will have an infinite loop. this could be solved by passing in a counter and once it reaches the counter of depth, just return.
foreach($r_ids as $r_id){
$ret[] = Relations::getLinks($r_id);
}
return $ret;
}
}
with depth limitor
class Relations {
public static function getLinksFromDB(relation_id){
return $db->array(); // return an array of matches based on the passed in $relation_id from the database, using your normal query here.
}
public static function getLinks(relation_id, $counter){
if($counter <= 0){
return [];
}
$ret = [];
$r_ids = Relations::getLinksFromDB(r_id);
foreach($r_ids as $r_id){
$ret[] = Relations::getLinks($r_id, $counter - 1);
}
return $ret;
}
}
both can be called as such:
$ids = ['A', 'B', 'C'];
$links = [];
foreach($ids as $id){
$links[] = Relations::getLinks($id);
}
or with the depth limit
$ids = ['A', 'B', 'C'];
$links = [];
foreach($ids as $id){
$links[] = Relations::getLinks($id, 20);
}
I am writing a follower module to my site and I got some problems with it. I want to list all the useres but only those ones that are not followed by me.
I use this function to get them:
**
* Listing users
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* #todo Valamiért hat kétjegyű az követendő user id akkor a scope lehal
*/
public function listUsers() {
//Declare an empty array for the result of the collection
$ids = array();
$collection = $this->user->followers;
$collection->each(function($follower) use (&$ids) {
$ids[] = explode(',', $follower->id . ',');
});
$users = User::Pending($ids)->get();
dd($users);
return view('profile.listUsers', [
'users' => $users,
'_user' => $this->user,
]);
}
It works fine with the ids from 0 - 9 when I use without explode but with ids from 10 - ... it kills my Pending scope.
The goal would be that add some char into the end of the id (in our case it is ,) and explode it. I did that but it kills my scope from the very begining.
What do you think, what could be possibly wrong? Am I using it absolutely wrong?
Thank you for your answers!
Okay, I simply use a foreach before my scope, put every element into an array and it works like a charm!
foreach($collection as $follower) {
$ids[] = $follower->id;
}
In my migration file, I gave my table pages a enum field with 2 possible values (as seen below). My question is, if it's possible to select these values with Laravels Eloquent?
$table->enum('status', array('draft','published'));
There are several Workarounds that I found, but there must be some "eloquent-native" way to handle this. My expected output would be this (that would be perfect!):
array('draft','published')
Thank you in advance!
Unfortunately, Laravel does not offer a solution for this. You will have to do it by yourself. I did some digging and found this answer
You can use that function and turn it into a method in your model class...
class Page extends Eloquent {
public static function getPossibleStatuses(){
$type = DB::select(DB::raw('SHOW COLUMNS FROM pages WHERE Field = "type"'))[0]->Type;
preg_match('/^enum\((.*)\)$/', $type, $matches);
$values = array();
foreach(explode(',', $matches[1]) as $value){
$values[] = trim($value, "'");
}
return $values;
}
}
And you use it like this
$options = Page::getPossibleStatuses();
If you want you can also make it a bit more universally accessible and generic.
First, create a BaseModel. All models should then extend from this class
class BaseModel extends Eloquent {}
After that, put this function in there
public static function getPossibleEnumValues($name){
$instance = new static; // create an instance of the model to be able to get the table name
$type = DB::select( DB::raw('SHOW COLUMNS FROM '.$instance->getTable().' WHERE Field = "'.$name.'"') )[0]->Type;
preg_match('/^enum\((.*)\)$/', $type, $matches);
$enum = array();
foreach(explode(',', $matches[1]) as $value){
$v = trim( $value, "'" );
$enum[] = $v;
}
return $enum;
}
You call this one like that
$options = Page::getPossibleEnumValues('status');
Made a small improvement to lukasgeiter's function. The foreach loop in his answer is parsing the string. You can update the regex to do that for you.
/**
* Retrieves the acceptable enum fields for a column
*
* #param string $column Column name
*
* #return array
*/
public static function getPossibleEnumValues ($column) {
// Create an instance of the model to be able to get the table name
$instance = new static;
// Pulls column string from DB
$enumStr = DB::select(DB::raw('SHOW COLUMNS FROM '.$instance->getTable().' WHERE Field = "'.$column.'"'))[0]->Type;
// Parse string
preg_match_all("/'([^']+)'/", $enumStr, $matches);
// Return matches
return isset($matches[1]) ? $matches[1] : [];
}
This throws an error if the column does not exist. So I added a small check in the code
public static function getPossibleEnumValues ($column) {
// Create an instance of the model to be able to get the table name
$instance = new static;
$arr = DB::select(DB::raw('SHOW COLUMNS FROM '.$instance->getTable().' WHERE Field = "'.$column.'"'));
if (count($arr) == 0){
return array();
}
// Pulls column string from DB
$enumStr = $arr[0]->Type;
// Parse string
preg_match_all("/'([^']+)'/", $enumStr, $matches);
// Return matches
return isset($matches[1]) ? $matches[1] : [];
}
As of L5.17 Eloquent does not include this functionality, instead you need to fall back to native QL. Here's an example that will work with SQL and in one line - returning an array like you asked.
In the spirit of one liner complexity ;)
I threw this in one of my view composers - it fetches the column from the table, explodes it and assembles the values in an array.
I iterate over that in my views using a foreach.
explode (
"','",
substr (
DB::select(" SHOW COLUMNS
FROM ".(new \Namespace\Model)->getTable()."
LIKE 'colName'"
)[0]->Type,
6,
-2
)
);
So i have a string, representing several objects (tags in this case)
i.e.: "php,mysql,doctrine2"
Let's say my database already has "php" and "doctrine2".
Now i want the best way to add the missing elemets (in this case mysql).
Should i create an object for every element and just use persist/sync or something, or is there a better way?
I need all the objects at the end anyway to add them to a new object (with a simple many-to-many relation) anyway.
I'd be happy about any suggestions.
1) Pull out all your tag names with a single query into an array
2) Use array_filter along with a closure to detect tags not present in the dataset
3) Create an insert for the new tags
$currentTags = getCurrentTagsArray();
$newTags = explode(',', 'php,mysql,doctrine2');
$newTagsToSave = array_filter($currentTags, function($item) use ($newTags){
if (in_array($item, $newTags))
{
return false;
}
return true;
});
Or...
You can use Doctrine 2's ArrayCollection wrapper (\Doctrine\Common\Collections\ArrayCollection()) it has pretty much the same implementation above as a filter method (you still need to pass the closure).
$myCollection->filter($closure);
I had a similar problem where I had to synchronize an entity collection with an external source. However, my problem required not only additions, but also updates and deletes. I used code to diff the ArrayCollection with another array, and call CRUD methods add based on the differences. As far as I can tell from the docs, doctrine doesn't natively handle this. Average performance should be O(n) but takes some memory.
/**
* #param array $source - the array we are starting with
* #param array $new - the array we want to end with
* #param $fnHash - function used to determine object equality, not based on object id
* #param $fnUpdate - function to perform update of existing object, takes current object and new object as params
* #param $fnAdd - function to perform insert
* #param $fnDelete - function to perform delete
*/
public static function syncArrays(array $source, array $new,
$fnHash, $fnUpdate, $fnAdd, $fnDelete)
{
// make modifiable array copies mapped by hashes of the elements
$sourceKeys = array_map($fnHash, $source);
$hasKeys =count($sourceKeys) > 0;
$newarray = ($hasKeys) ? array_combine(array_map($fnHash, $new), $new) : $new;
if ($hasKeys) { // true => may have updates or deletes
$sourcearray = array_combine($sourceKeys, $source);
// updates
foreach ($sourceKeys as $hashkey) {
if (isset($sourcearray[$hashkey]) && isset($newarray[$hashkey])) {
$fnUpdate($sourcearray[$hashkey], $newarray[$hashkey]);
unset($sourcearray[$hashkey]);
unset($newarray[$hashkey]);
}
}
// deletes
foreach ($sourcearray as $entity) {
$fnDelete($entity);
}
}
//adds
foreach ($newarray as $entity) {
$fnAdd($entity);
}
}
The way I call it to update my doctrine association $parentEntity->getPayments() is:
ArrayHelper::syncArrays($parentEntity->getPayments()->toArray(), $newPayments,
function($entity) {return $a->getName();}, // hash function
function($current, $new) {
$current->setTotal($new->getTotal()); // update function
},
function($a) use ($parent, $manager) {
$parent->addVendorPaymentObject($a); // add function
$manager->persist($a);
},
function($a) use ($manager) { // delete function
$manager->remove($a);
}
);
In Magento, if you need to get / fetch the Shopping Cart's Item details, you can do it in any of the two possible ways, which will provide you with all the shopped Items in an array:-
$cartItems1 = $cart->getQuote()->getAllItems();
$cartItems2 = $cart->getItems()->getData();
But before using any one of the above two methods, you need to initialize the shopping cart object as:-
$cart = new Mage_Checkout_Model_Cart();
$cart->init();
Can anyone please describe in details as to what the two options provide & their differences between each other, along with their possible usage.
In any more such option is available in Magento, can anyone please highlight it?
If you look at the code of the Cart and Quote classes everything will become clear.
Here's the code for $cart->getItems():
public function getItems()
{
return $this->getQuote()->getAllVisibleItems();
}
Plain and simple - it just calls a method of the Quote object. So the question now is: What is the difference between getAllVisibleItems() and getAllItems()?
Let's look at the code of both methods:
public function getAllItems()
{
$items = array();
foreach ($this->getItemsCollection() as $item) {
if (!$item->isDeleted()) {
$items[] = $item;
}
}
return $items;
}
public function getAllVisibleItems()
{
$items = array();
foreach ($this->getItemsCollection() as $item) {
if (!$item->isDeleted() && !$item->getParentItemId()) {
$items[] = $item;
}
}
return $items;
}
The only difference: getAllVisibleItems() has an additional check for each item:
!$item->getParentItemId()
which tests if the product has a parent (in other words, it tests if it is a simple product). So this method's return array will be missing simple products, as opposed to getAllItems().
Are there any other ways to retrieve items?
One would be to directly get the product collection from the quote object:
$productCollection = $cart->getQuote()->getItemsCollection();