Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by thetuningspoon

  1. @bernhard I've finally had a chance to try out your module tonight for a project where we're loading pages into a large data table (200+ rows) and were hitting a wall. Using RockFinder I now have the initial page load down to ~2 seconds, down from ~7+ seconds! This is a fantastic module, Bernhard. It looks like it's really well thought out and has some really powerful features for building queries. I love how it extends PW's native selectors and allows you to return standard objects, making it easier to substitute this in for a regular $pages->find. Thank you for making this! I think I can answer my own question now... The main issue with creating Page objects is that page instantiation requires a trip back to the database. The initial $pages->find converts a selector into a SQL query which returns an array of matching page IDs. Then those IDs are used to go back to the database and get the pages (or pull them from cache if they're already loaded). Then for any page field requested that isn't auto-join, an additional database query is required. If you're looping through a lot of pages, that's a lot of DB queries! It seems like there might be a way to provide the functionality of RockFinder in the native PW core, as an option when loading pages. You would still end up with Page objects in the end (which in my case would be a huge boon since I like to extend the Page class with a custom class and methods for each template), but we could skip that second trip to the database (getting pages by IDs) if we could just tell PW which fields we wanted it to get up front. After that, any additional fields we didn't specify could be loaded with another trip to the DB, as they are now. That being said, I'm sure @ryan has a good reason for that second trip to the DB. But it seems like there must be a way that we could improve the speed of native pages, even if it is a hidden/advanced option with some caveats. One minor complaint: I noticed is that the module seems to fail silently and return nothing when it can't find one of the fields. It would be good to throw an exception to make this easier to debug. Edit: Another thought... Is there a reason not to use WireData and WireArray for the objects returned from RockFinder, in place of an StdObject? This would allow you to use WireArray's built in sorting and selecting features on the result set: $results = $this->database->query($this->getSql()); if($array) { $objects = $results->fetchAll(\PDO::FETCH_ASSOC); } else { $objects = $results->fetchAll(\PDO::FETCH_CLASS, '\ProcessWire\WireData'); $objects = (new WireArray())->import($objects); }
  2. Why is it that creating page objects is so much slower/memory intensive than a multidimensional array? I could understand if the pages were loading all their fields, but most fields are not auto-join 🤔
  3. @LostKobrakai This is on a production server. @dragan There are no files or images. @teppo We are creating a box subscription service where the user can have multiple boxes and configure what is included in each of them. The page structure is: - Subscription (Not an actual user page, just linked to the user by a page field) - - Boxset v1 <-- This is the page I'm calling clone on, and the resulting clone is stored as a sibling - - - Box 1 - - - Box 2 - - - Box 3 - - - Box 4 - - Boxset v2 - - - Box 1 - - - Box 2 - - - Box 3 ... etc There are not many fields on each page (~3-4). But each Box has a repeater field on it which usually contains 6 entries. The repeater is using AJAX to generate new items. The 30 pages number I came up with included the repeater pages. I've switched to using a ProFields Table field instead of a repeater since I think it will actually serve us just as well in this case. The clone time is now greatly reduced, although still feels a bit slow to me. It does sound like I should set up a test case and some debugging to see if something weird is going on. Thanks everyone for your feedback and let me know if you have any further thoughts!
  4. In an app I'm building I frequently need to clone a set of pages that represent a user's 'order'. Every time they make changes to the order, I want to make a new version by cloning this page structure. The pages in the structure also contain repeaters, so while there is in theory no limit to the number of pages in the structure, there would realistically be no more than about 30 pages (repeaters included). From a coding perspective the clone() method makes this all a breeze. Unfortunately the clone operation just takes too long (~20 seconds). Does any one have any insight into why this is so slow? Is there any way around this aside from reducing the number of pages? Is there hope that we might be able to make this more efficient in a future version of PW?
  5. I find it ironic that a product called "webpack" is now touting the advantages "code-splitting"🤔 It's like we're adding layer on top of layer of complexity just to get back to where we started.
  6. For some reason I never really thought about the fact that, with client-side rendering, you are basically sending your entire application over the wire. It's like having to install an app each time you visit a website. Makes me feel more confident in the componentized server-side approach I've been pursuing, which seems to achieve 90% of what the fully client-side approach aims to achieve, without the complexity and overhead. And it still leaves room for plugging in a more progressive framework like Vue for the cases when you need that extra 10% of interactivity.
  7. This is now possible using owner selectors! http://processwire.com/blog/posts/processwire-3.0.95-core-updates/ $pages->find('template=repeater_collections_detail_images, your_repeater_field.owner.collections_detail_designer=123, include=hidden, sort=name');
  8. This can now be done with owner selectors (http://processwire.com/blog/posts/processwire-3.0.95-core-updates/) $tagsThatHaveBeenUsedOnPosts = $pages->find('template=tag, tags.owner.template=post'); Where tags is the name of the field on the post template that holds the tag pages. Also, for any given tag page you can check how many post pages reference it with $page->references('template=post')->count(); https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#what-pages-point-to-this-one
  9. @netcarver Thanks for chiming in. I just submitted a pull request on GitHub. Looks like I had submitted a bug report at the time, which this pull request resolves. I also added a password config option, and made some change to the read() method (but I don't remember what it's doing exactly).
  10. An update on this for others: Using Redis for sessions solved the problem for me. I had to make some changes to the module to get it to work right (if anyone wants my code, let me know). Today I happened to try migrating my project from using ProcessWire's default MyISAM database engine to using InnoDB (had to convert all tables and set $config->dbEngine in site/config.php). I was playing around with the demo version of my system (which is not using Redis). And interestingly enough, I THINK this (in combination with SessionHandlerDB) actually resolved the issue with simultaneous ajax calls! Perhaps this is because InnoDB has row-level locking instead of table-level locking?
  11. You should be fine using the native $_SESSION, although PW also provides the $session api variable if you want to use it (store vars using $session->myVar = $myValue and retrieve with $session->myVar). I am suspicious that something else is causing your problem.
  12. Eyup, good point. Which is why this can come in handy.
  13. What I meant was whether PW was using the same basic flow: Converting a selector to an SQL select statement, getting the IDs of the matching pages, and then calling getById() to load the actual pages into a PageArray. I spent this morning doing a deep dive into the core and have confirmed that this is how it's working. I was also able to simplify the example by @LostKobrakai to the following: $pf = $this->pages->getPageFinder(); $query = $pf->find($selector, ['returnQuery' => true]); # Show sql //$query->getQuery(); # Modify query //$query->where($sql); $statement = $query->execute(); $statement->execute(); # Load the pages $ids = array(); while($row = $statement->fetch()) $ids[] = $row[0]; $myPages = $this->pages->getById($ids); I haven't solved the pagination side of things yet. Unfortunately PagesLoader::find() is doing quite a bit of work that we're not able to take advantage of due to the fact that we have to bypass it completely and go straight to PagesLoader::find() in order to get the DatabaseQuerySelect() object. I'm not sure if this problem can be solved without modifying the core or duplicating a lot of its code. For future reference, this is the basic flow of a Pages::find() call (sans various options and some intermediary methods): Pages::find() Does nothing on its own. Delegates to PagesLoader::find() PagesLoader::find() Checks for page in runtime cache, returns cached page if it's in memory Creates a selector object from your selector string (PageFinder::find() can also do this, as I discovered) PageFinder::find() Turns selector object/string into a ProcessWire DatabaseQuerySelect object (via PageFinder::getQuery()) Turns DatabaseQuerySelect into an SQL select statement and queries the database Returns a multidimensional array with the ID, parent ID, and template ID of each matching page (OR a ProcessWire DatabaseQuerySelect object if the $returnQuery option is true) PagesLoader::getById() Takes an array of IDs Creates a page object for each ID and populates it with fields from the database (an additional database query). This is where any autojoin fields are pulled from the database. PagesLoader::find() Sorts pages by template (?) Sets up pagination Returns final PageArray
  14. This seems to do it: $pf = $this->pages->getPageFinder(); $selector = new Selectors($selector); $query = $pf->find($selector, ['returnVerbose' => true, 'returnQuery' => true]); $statement = $query->execute(); $statement->execute(); $ids = array(); while($row = $statement->fetch()) $ids[] = $row[0]; $myPages = $this->pages->getById($ids); Is this how PW constructs the PageArray during a regular $pages->find()? So even if you were autojoining all your fields, it is still doing one query to find the matching pages and then another separate query for each page to load the desired fields?
  15. Yes, I understand. I meant how does it work under the hood. I guess path is a dynamic page property, so it requires constructing the page object to get it? You mean with a $pages->get()? Wouldn't that mean going back to the database again to build each page? Anyway, thank you for explaining further. At this point I am wondering if you can provide any insight to my original question, which was how I can modify the sql of a regular $pages->find() and then return the results as a PageArray.
  16. @bernhard How does the closure thing work? Is it creating a page object for each result? Can you give an example of when the closure would be required? Thanks
  17. @bernhard This looks very cool, but I am not totally clear on what it does and how it does it. Can you give me a semi-technical explanation of how your module works (inputs and outputs) and where it plugs into the core? What exactly does a call to RockFinder return? Is there a way to get page objects from the results if I don't need the scalability features of the module and just want a normal page array in the end? Can I still use pagination with the results? Also, as cool as your module looks (and it looks very cool), if what I'm wanting to do can be achieved easily with just the core, I'd prefer to keep it simple.
  18. @LostKobrakai I've been playing around with your example here and it's really neat. I've been looking for a way to add or modify a selector with custom SQL. It looks like your onto something here. However, I can't figure out what to do after running the query. How to I convert the results into a PageArray? Also, I noticed in studying the core a bit that this technique bypasses some of what would normally occur in a $pages->find(), including (I think) setting up the pagination and the loadOptions. In my particular use case I have a selector that does exactly what I want, but need to add a custom ORDER_BY for a more complex sort than PW allows.
  19. I get that, but if everyone is using their own helper libraries and different frameworks, you lose the great jQuery ecosystem and all the plugins and libraries built on top of it. You could end up having to include a bunch of code from different libraries that are doing the same thing in order to use the existing solutions you want. That feels like a step backward to me. I don't see why jQuery can't just be retooled so that it's using newer, faster technology under the hood, with the same compact, standardized api on the front end. Maybe have a legacy jQuery and a new, slimmer jQuery for those that don't need old browser support. Regarding jQuery and more advanced JS applications, I don't think jQuery should be thought of as competing against vue or react. It's at a lower level in the stack. It doesn't impose structure on your application, but that doesn't mean it prevents you from creating your own structure. Disclaimer: These are just my feelings as a developer who is still trying to build my applications largely server-side, where my ajax calls usually return blocks of html encoded within JSON instead of pure data structures. This keeps all rendering and business logic server side in order to keep the application code simple and DRY. Not sure if I really know enough about what I'm talking about when it comes to the new client-side development paradigm. I feel like both are reasonable approaches to development (depending on the application's requirements). It's when you don't choose one or the other that things really get messy and WET.
  20. Sorry to say, whenever I see the vanilla JS alternatives to jQuery, it reminds me why I still use jQuery
  21. @mel47 Thanks. I just pushed another release so the module will only load in the admin, as it was before. But it is still no longer limited to specific admin processes.
  22. Just released an update on GitHub: -Added support for repeaters using the new InputfieldPage::renderReadyHook method (Will fall back to hooking into render if using on an older version of PW) -Specifying processes is no longer required (I'm not sure why I limited the modules use to just specific admin pages before, but there may have been a reason. Let me know if you see any problems with this) I looked into the possibility of making the modal optional, but it's not such a simple task with the way the JS is currently structured. I would like to do some refactoring, but not sure if/when I'll have the time.
  23. @Kiwi Chris Did you try this? https://processwire.com/talk/topic/9857-module-page-field-edit-links/
  24. Hey guys, for some reason the forum has not been notifying me of new replies in this thread, so I apologize for my absence. @Robin S Thanks for the info on the new InputfieldPage::renderReadyHook method. I will update the module asap. I think that as it stands right now, the module does work in a repeater as long as the main page also has a page field that is using the module. The problem is that the js and css assets are not included by the repeater. @Ipa it looks like the javascript for these two modules is conflicting. I am not familiar with HelperFieldLinks. Can you explain its purpose? Also, can you get me a larger screen shot of this in context of the rest of the field? I don't think this would be too difficult to add. Good idea.
  25. Not a question, but just something I discovered that I wanted to share on the forum since I haven't seen it discussed anywhere. If you are familiar with the "autojoin" option on individual ProcessWire fields, enabling this feature means that every time a page with that field on it is loaded into memory, the field will be included with it immediately. (Normally just the page's name and meta data is included, and getting a specific field from the page requires a separate trip to the database. This is to conserve memory) Normally this is fine and it keeps things simple, but occasionally you may be loading a lot of data in a consistent fashion (for, example, to populate a table) and know exactly which fields you need. With the autojoin option on fields, you would have to enable autojoin for all pages at all times, which you probably don't want. With the following code, you can do a $pages->find() and specify exactly which fields you want to autojoin with it: $pages->find("template=whatever", ['loadOptions' => ['joinFields' => ['filed1','field2','field3']]] ); I tested out in debug mode on one of my projects, and it seemed to significantly reduce the number of SQL queries required for an html data table I was building. Hope that helps someone!
  • Create New...