Export objects from class issue - php

I have a product export and I try to export all objects using my custom plugins. I'm using below method to load all objects
$list = new Pimcore_Model_Object_Bodproduct();
$list->setOrder ( "ASC" );
$list->setOrderKey ( "o_id" );
// Load all filtered objects - products
$list->load ();
Then i'm using foreach loop to access the objects.
It's working fine. But the problem is it's taking lot of time to export. Is there any other solutions for this .. I mean instead of load() function, is there any other method is available?

You can use $list->loadIdList() to fetch only object IDs - which should be faster. Then you can call Object::getById($id) within loop to provide progress bar (assuming your script is launched in console).
In fact this is what load() is doing internally, please see Object\Listing\Resource class
Another option is to split load to many "pages" by calling $list->getItems($offset, $itemCountPerPage).

In Pimcore object listing, you can traverse each object in list without calling a load method. In your case, just remove "$list->load ();" and see it will work with less time.

Related

What's the best way to edit (also add and delete) array data in the SS CMS?

I am trying to get a gridfield to display an array of data, instead of a SS_List of DataObjects. So I decode a JSON string to create an array of class instances and would like to show a (few) fields of those objects in the rows of the gridfield. I am not using ordinary DataObjects to prevent versioned relations to get out of sync, so I JSON the related data on the parent object.
If there is an easier solution than a gridfield: I'm all ears!
So, how can I load a gridfield up with (array) data for it's rows? Or, what's the best way to edit (also add and delete) array data in the SS CMS?
Reading through the source and API didn't get me far, so that's why I ask you!
If you use an ArrayList containing ArrayData entries you should have some luck. ArrayList implements SS_List, and you should just disable any components that involve edit, add, remove etc via your GridFieldConfig (or just use an empty one to start with).
$myList = ArrayList::create();
foreach ($yourData as $values) {
$myList->push(ArrayData::create($values));
}
$gridField = GridField::create('MyGrid', null, $myList, GridFieldConfig_Base::create());

Passing Wordpress objects from parent to popup child using PHP SESSION

I am passing an array of WP objects (ie, WP_User) from Wordpress site to a popup window using $_SESSION variable.
When popup is open, I var_dump($_SESSION['variable']['WP_Users']) and everything looks good.
However, when I am trying to access $_SESSION['variable']['WP_users'][0]->data->parameter, all values are NULL.
Aside from that, error_log says:
"main(): The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "WP_User" of the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition in /ABSPATH/popup_template.php line 52"
As I understand, my popup has no definitions of WP classes, such as WP_User etc., therefore I am unable to manipulate WP objects.
Any ideas on how to tackle this? (keep in mind I am not using (un)serialize() at all)
So solution was actually pretty straightforward: I had to do what error_log asked.
In parent window I had to serialize array of Wordpress objects before shoving them into the SESSION var like so:
$_SESSION['variable'] = serialize( $array_of_WP_objects );
To my popup template I had to add definition of WP_User from wp_includes folder, and then unserialize my array back to $array_of_WP_objects like so
require_once( '../../../../wp-includes/class-wp-user.php' );
$array_of_WP_objects = unserialize( $_SESSION['variable'] );
Worked like a charm.

First use of array is slow

I have encountered a weird "bug" in PHP and since I'm a novice I'm at the end of my knowledge.
I'm developing a TYPO3 extension that has some major performance issues with data, or so I thought.
It turns out that the first use of the array, which stores all the objects I got from my database query, is taking way to long.
Every use or loop after that is fast again.
The code looks like this:
$productsArr = $this->productRepository->findByDetail($category, $properties);
$newSortArr = array();
$familyProductList = array();
$counter = count($productsArr);
/** #var Product $product */
for($i = 0; $i < $counter; $i++) {
//it takes to long to do this
$product = $productsArr[$i];
if(!empty($productsArr[$i])) {
$newSortArr[$product->getInFamily()->getUid()][] = $product;
}
}
It doesn't matter where I first use the object array. The first use of the array is always taking around 30 sec.
Has anybody encountered something similar?
If you need more information I will gladly provide that.
Thanks in advance!
Your $productsArr is not an array but an Object of Exbase's class QueryResult which you can iterate over with foreach or do index access. This Object execute the query and build it's objects only when needed, so at the moment you do $product = $productsArr[$i];, all Product-objects of $productsArrare built. The major problem is that building objects in PHP has bad performance and consumpts a lot of memory.
So, to avoid the performance issue, consider using a custom query with
$this->productRepository->createQuery()->statement('select * from ...')->execute();
to get exactly what you want instead of loading a huge amount of objects and refine them later in PHP.
As Jay already mentioned, your result isn't an array, but a QueryResult. Just FYI, it's possible to transform it to an array by adding ->toArray() at the end of your query:
$productsArr = $this->productRepository->findByDetail($category, $properties)->toArray();
But this won't improve the situation. There are two possible issues:
Iterating all objects
The advantage of a QueryResult is that it reflects only the result of the query but doesn't resolve all objects already. A QueryResult can be passed e.g. to a Pagination widget and will then only load the results requested (e.h. 1-10, 11-20 etc.).
Since you're applying manual sorting, all your objects (depending on your project this can be a lot...) are loaded.
Apparently you would like to sort the products by their family UID? Why not do that with Extbase functionality in your ProductRepository:
protected $defaultOrderings = array(
'inFamily.uid' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
Eager loading of sub objects
Your model Product might have relations to other models (e.h. Product to Category, Product to Options etc.). By default, Extbase resolves all these relations on accessing the objects.
To prevent this, you can use Lazy Loading for relations. This makes sense for sub objects that are not used in all the views. E.g. in your list view you only need to title, image and price of your product, but you don't need all options of the product.
To configure lazy loading for these sub objects, you just need to #lazy annotation in the model:
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\My\Extension\Domain\Model\ObjectStorageModel>
* #lazy
*/
protected $categories;
/**
* #var \My\Extension\Domain\Model\OtherModel
* #lazy
*/
protected $author;
Lazy loading can have some drawbacks, e.g. in certain situations when checking for an object being an instance of OtherModel, you get an object of type LazyLoadingProxy instead. You can work around most of these issues or maybe don't even stumple upon them in normal scenarios. A common workaround if you really depend an object on not being a LazyLoadingProxy is a check like that:
if ($product->getAuthor() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
$product->getAuthor()->_loadRealInstance();
}
This makes sure that in any case you have a "real" instance of the object.
Please don't forget to flush system caches when you're doing a change regarding either one of the issues.
I assume the array is populated on the first line. Have you considered populating $product using foreach($productArr as $product) instead o using for?

returning values from function or method multiple times by only calling the class once

I have a members.php file that shows my websites members. I echo members name by using foreach method. A method of Members class returns an array, then I use foreach loop in the members.php file to echo the members. I am trying to aovid writing as less php code as possible in my members.php file where my html files are located. Is there a way to avoid using foreach inside members.php file?
For example, is it possible to return value from a method couple of times? (by only calling the object once). Just like how we normally call the functions? This question doesn't make sense, but I am just trying to see if there is a away around this issue?
You could write an intermediate function that caches whatever the Members-method returns and return just one index from the cache, specified by a parameter. But then you are back to using a some kind of loop.
There's nothing wrong with a loop in your view. After all, PHP is a templating language.
However, you could write an object method to return/print a formatted members list.

Reduce database calls for php web shop

I'm looking for a way to prevent repeated calls to the database if the item in question has already been loaded previously. The reason is that we have a lot of different areas that show popular items, latest releases, top rated etc. and sometimes it happens that one item appears in multiple lists on the same page.
I wonder if it's possible to save the object instance in a static array associated with the class and then check if the data is actually in there yet, but then how do I point the new instance to the existing one?
Here's a draft of my idea:
$baseball = new Item($idOfTheBaseballItem);
$baseballAgain = new Item($idOfTheBaseballItem);
class Item
{
static $arrItems = array();
function __construct($id) {
if(in_array($id, self::arrItems)){
// Point this instance to the object in self::arrItems[$id]
// But how?
}
else {
// Call the database
self::arrItems[id] = $this;
}
}
}
If you have any other ideas or you just think I'm totally nuts, let me know.
You should know that static variables only exist in the page they were created, meaning 2 users that load the same page and get served the same script still exist as 2 different memory spaces.
You should consider caching results, take a look at code igniter database caching
What you are trying to achieve is similar to a singleton factory
$baseball = getItem($idOfTheBaseballItem);
$baseballAgain =getItem($idOfTheBaseballItem);
function getItem($id){
static $items=array();
if(!isset($items[$id])$items[$id]=new Item($id);
return $items[$id];
}
class Item{
// this stays the same
}
P.S. Also take a look at memcache. A very simple way to remove database load is to create a /cache/ directory and save database results there for a few minutes or until you deem the data old (this can be done in a number of ways, but most approaches are time based)
You can't directly replace "this" in constructor. Instead, prepare a static function like "getById($id)" that returns object from list.
And as stated above: this will work only per page load.

Categories