发现存储特定面向对象数据结构的最佳方法

2024-01-22

经过一些奇妙的建议,以及由于最终有可能解决我的问题而兴奋的不眠之夜,我意识到我仍然没有完全找到解决方案。因此,我在这里更详细地概述我的问题,希望有人知道实现这一目标的最佳方法。

回顾一下(如果你还没有读过上一篇文章 https://stackoverflow.com/questions/28482648/how-to-store-object-based-data-in-a-database-so-it-remains-queryable):

  • 我正在从头开始构建一个 PHP OOP 框架(在这件事上我别无选择)
  • 该框架需要以尽可能最有效的方式处理面向对象的数据。它不需要快如闪电,它只需要成为问题的最佳解决方案即可
  • 对象非常类似于严格编写的 oop 对象,因为它们是特定类的实例,该类包含一组严格的属性。
  • 对象属性可以是基本类型(字符串、数字、布尔值),但也可以是一个对象实例或一组对象(限制是该数组必须是同一类型的对象)

最终,存储引擎支持面向文档的存储(类似于 XML 或 JSON),其中对象本身具有严格的结构。

我不会概述到目前为止我已经尝试过的事情(我在上一篇文章中简要讨论了这一点),我将在这篇文章的其余部分详细解释我正在尝试做的事情。这篇文章会很长(抱歉!)。


首先,我需要讨论一个术语,我必须引入该术语来解决这组需求带来的最关键的问题之一。我把这个术语命名为“坚持”。我知道这个术语在处理对象数据库时确实有不同的含义,因此我愿意接受关于不同术语的建议。但现在,让我们继续前进。

坚持

持久性是指对象的独立性。我发现在考虑从 XML 生成的数据结构时需要引入这个术语(这是我必须能够做到的)。在 XML 中,我们看到完全依赖于其父对象的对象,同时我们也看到可以独立于父对象的对象。

下面的示例是符合特定结构的 XML 文档示例(例如 .wsdl 文件)。每个对象都类似于具有严格结构的类型。每个对象都有一个“id”属性

在上面的示例中,我们看到两个用户。两者在其“地址”属性下都有自己的地址对象。但是,如果我们查看他们的“favouriteBook”属性,我们可以看到他们都re-use相同的 Book 对象实例。另请注意,这些书使用同一作者。

所以我们有 Address 对象非持久因为它只与其父对象(用户)相关,这意味着它的实例只需要在拥有的用户对象存在时存在。然后是 Book 对象执着的因为它可以在多个位置使用并且它的实例保持持久性。

起初,我觉得提出这样的术语有点疯狂,但是,我发现它非常容易理解和实际使用。它最终将“多对多、一对多、一对一、多对一”公式浓缩为一个简单的想法,我认为这种想法对于嵌套数据来说效果更好。

我在这里对上述数据进行了图像表示:

根据我对持久性的定义,有一组规则可以帮助理解它。这些规则如下:

  • 更新/创建
  • 正在存储的基础对象的持久子对象update持久对象的属性,最终更新其实例。
  • 非持久对象always创建对象的一个​​新实例,以确保它们始终使用非持久实例(在任何给定时间,两个非持久实例都不会存在于多个位置)
  • deleting
  • 基础对象的持久子对象不会被递归删除。这是因为持久对象可能存在于其他地方。您总是会直接删除持久对象。
  • 基础对象的非持久子对象将与基础对象一起删除。如果不将它们移除,它们就会陷入困境,因为它们的设计要求它们有一个父级。
  • 检索
  • 由于持久性主要定义了修改的工作方式,因此除了您期望持久性如何影响模型的存储方式以及如何检索模型之外,检索并不涉及太多的持久性(持久性对象实例无论位于何处都保持持久性,非持久对象总是有自己的实例)

在我们继续之前要注意的最后一件事 - 数据模型的持久性是由模型本身定义的而不是关系。最初,持久性是关系的一部分,但当系统期望您了解模型的结构以及它们的使用方式时,这是完全没有必要的。最终,模型的每个模型实例要么是持久的,要么不是。


因此,现在看一些代码,您可能会开始看到疯狂背后的方法。虽然这个解决方案的原因似乎是能够围绕符合一组条件的客观数据构建一个存储系统,但它的设计实际上来自于希望能够存储类实例,和/或从客观的数据结构。

我编写了一些伪类作为我尝试生成的功能的示例。我已经注释了大多数方法,包括类型声明。

首先,这将是所有模型类将扩展的基类。此类的目的是在模型类/对象和数据库/存储引擎之间创建一个层:

<?php
/**
 * This is the base class that all models would extend. It contains the functionalities that are useful among all model
 * objects, such as crud actions, finding, and crud event management.
 *
 * @author Donny Sutherland <[email protected] /cdn-cgi/l/email-protection>
 * @package Main
 * @subpackage Sub
 *
 * Class ORMModel
 */
class ORMModel {
    /**
     * In order to generate relationships between objects, every object MUST have an id. This functions as the object's
     * unique identifier. Each object in it's model type (collection) has it's own id.
     *
     * @var int
     */
    public $id;
    /**
     * Internal property assigned by the application. This is where the persistence of the model is defined.
     *
     * @var bool
     */
    protected $internal_isPersistent = true;
    /**
     * Internal property assigned by the application. This is an array of the model's properties, and their PHP type.
     *
     * For example, a User model might use something like this:
     * array(
        "id" => "integer",
     *  "username" => "string",
     *  "password" => "string",
     *  "address" => "object",
     *  "favouriteBook" => "object",
     *  "allBooks" => "array"
     * )
     *
     * @var array
     */
    protected $internal_propertyTypes = array();
    /**
     * Internal property assigned by the application. This is an array of the model's properties which are objects, and
     * the MODEL CLASS type of the object.
     *
     * For example, the User model example for the property types might use this:
     * array(
     *  "address" => "Address",
        "favouriteBook" => "Book",
     *  "allBooks" => "Book"
     * )
     *
     * @var array
     */
    protected $internal_objectTypes = array();
    /**
     * I am not 100% sure on the best way to use this yet, I have tried a few different ways and all seem to cause
     * performance problems. But ultimately, before we attempt to update an object, we cache it's currently stored
     * instance to this property, allowing us to compare old vs new. I find this really useful for detecting whether a
     * property has changed, I just need to work out the best way to do it.
     *
     * @var $this
     */
    protected $internal_old;

    /**
     * The lazy way to construct an empty model object (all NULL values)
     *
     * @return $this
     */
    final public static function constructEmpty() {

    }

    /**
     * This method is used by the other constructFromXXX methods once the data has been converted to a PHP array.
     * This method is what allows us to build a RESTful interface into the ORM system as it conforms to the following
     * rules:
     *
     * - if the id is set (not null), first pull the object from storage.
     * - For each key => value of the passed array, OVERWRITE the value
     * - For properties that are model objects/arrays, if the property is assiged to the array:
     *  - if the array value is NULL, we are clearing the object relationship
     *  - if the array valus is not null, construct recursively at this point
     *
     * Ultimately, if you assign a property in the array that you pass to this method, it will overwrite the value. If
     * you do not, it will use the property value in storage.
     *
     * @param array $array
     *
     * @return $this
     */
    final public static function constructFromArray(array $array) {

    }

    /**
     * This method attempts to decode the value of $json into a PHP array. It then calls constructFromArray if the string
     * could be decoded.
     *
     * @param $json
     *
     * @return $this
     */
    final public static function constructFromJson($json) {

    }

    /**
     * This method attempts to decode the value of $xml into a PHP array. It then calls constructFromArray if the xml
     * could be decoded.
     *
     * @param $xml
     *
     * @return $this
     */
    final public static function constructFromXml($xml) {

    }

    /**
     * Find one object, based on a set of options.
     *
     * @param ORMCrudOptions $options
     *
     * @return $this
     */
    final public static function findOne(ORMCrudOptions $options) {

    }

    /**
     * Find all objects, (optionally) based on a set of options
     *
     * @param ORMCrudOptions $options
     *
     * @return $this[]
     */
    final public static function findAll(ORMCrudOptions $options=null) {

    }

    /**
     * Find the count of objects, based on a set of optoins
     *
     * @param ORMCrudOptions $options
     *
     * @return integer
     */
    final public static function findCount(ORMCrudOptions $options) {

    }

    /**
     * Find one object, based on it's id, and (optionally) a set of options.
     *
     * @param ORMCrudOptions $options
     *
     * @return $this
     */
    final public static function findById($id,ORMCrudOptions $options=null) {

    }

    /**
     * Push this object to storage. This creates/updates all of the contained objects, based on their id's and
     * persistence.
     *
     * @param ORMCrudOptions $options
     *
     * @return bool
     */
    final public function pushThis(ORMCrudOptions $options) {

    }

    /**
     * Pull this object form storage. This retrieves all of the contained objects again, based on their id's and
     * persistence.
     *
     * @param ORMCrudOptions $options
     *
     * @return bool
     */
    final public function pullThis(ORMCrudOptions $options) {

    }

    /**
     * Remove this object from storage. This conditionally removes the contained objects (based on persistence) based
     * on their id's.
     *
     * @param ORMCrudOptions $options
     */
    final public function removeThis(ORMCrudOptions $options) {

    }

    /**
     * This is a crud event.
     */
    public function beforeCreate() {

    }

    /**
     * This is a crud event.
     */
    public function afterCreate() {

    }

    /**
     * This is a crud event.
     */
    public function beforeUpdate() {

    }

    /**
     * This is a crud event.
     */
    public function afterUpdate() {

    }

    /**
     * This is a crud event.
     */
    public function beforeRemove() {

    }

    /**
     * This is a crud event.
     */
    public function afterRemove() {

    }

    /**
     * This is a crud event.
     */
    public function beforeRetrieve() {

    }

    /**
     * This is a crud event.
     */
    public function afterRetrieve() {

    }
}

因此,最终,此类将被设计为提供构造、查找、保存、检索和删除模型对象的功能。内部属性是仅存在于类中(不存在于存储中)的属性。当您使用接口创建模型并向模型添加属性/字段时,这些属性由框架本身填充。

这个想法是,该框架带有一个用于管理数据模型的接口。使用此界面,您可以创建模型,并向模型添加属性/字段。这样做时,系统会自动为您创建类文件,并在您修改持久性和属性类型时更新这些内部属性。

为了让开发人员友好,系统为每个模型创建两个类文件。一个基类(扩展 ORMModel)和另一个类(扩展基类)。基类由系统操作,因此不建议修改此文件。开发人员使用另一个类向模型和 CRUD 事件添加附加功能。

回到示例数据,这里是 User 基类:

    <?php
class User_Base extends ORMModel {
    public $name;
    public $pass;
    /**
     * @var Address
     */
    public $address;
    /**
     * @var Book
     */
    public $favouriteBook;

    protected $internal_isPersistent = true;
    protected $internal_propertyTypes = array(
        "id" => "integer",
        "name" => "string",
        "pass" => "string",
        "address" => "object",
        "favouriteBook" => "object"
    );
    protected $internal_objectTypes = array(
        "address" => "Address",
        "favouriteBook" => "Book"
    );
}

几乎不言自明。再次注意,内部属性是由系统生成的,因此这些数组将根据您在模型管理界面中创建/修改用户模型时指定的属性/字段生成。另请注意地址上的文档块和favouriteBook属性定义。这些也是由系统生成的,使得这些类非常 IDE 友好。

这将是为用户模型生成的另一个类:

    <?php
final class User extends User_Base {
    public function beforeCreate() {

    }

    public function afterCreate() {

    }

    public function beforeUpdate() {

    }

    public function afterUpdate() {

    }

    public function beforeRemove() {

    }

    public function afterRemove() {

    }

    public function beforeRetrieve() {

    }

    public function afterRetrieve() {

    }
}

再次,非常不言自明。我们扩展了基类来创建另一个类,开发人员可以在其中添加其他方法,并向 CRUD 事件添加功能。

我不会添加构成其余示例数据的其他对象。因为上面应该解释了它们的外观。

因此,您可能/可能没有注意到,在 ORMModel 类中,CRUD 方法需要 ORMCrudOptions 类的实例。这个类对于整个系统非常重要,所以让我们快速浏览一下:

    <?php

/**
 * Despite this object being some-what aggregate, it it quite possibly the most important part of the ORM, in that it
 * defines how CRUD actions are executed, and outline how the querying is done.
 *
 * Class ORMCrudOptions
 */
final class ORMCrudOptions {
    /**
     * This ultimately makes up the "where" part of the sql query. However, because we want to be able to make querying
     * possible at any depth within the hierarchy of a model, this gets quite complicated.
     *
     * Previously, I developed a system which allowed the user to do something like this:
     *
     * "this.customer.address.postcode LIKE ('%XXX%') OR this.customer.address.line1 LIKE ('%XXX%')
     *
     * he "this" and the "." are my extension to basic sql. The "this" refers to the base model that you are finding,
     * and each "." basically drills down into the hierarchy to make a comparison on a property somewhere within a
     * contained model object.
     *
     * I will explain more how I did this in my post, I am most definitely looking at how I could better achieve this
     * though.
     *
     * @var string
     */
    private $query;
    /**
     * This allows you to build up a list of order by definitions.
     *
     * Using the orderBy method, you can chain up the order by statements like:
     *
     * ->orderBy("this.name","asc")->orderBy("this.customer.address.line1","desc")
     *
     * Which would be similar to doing:
     *
     * ORDER BY this_name ASC, this_customer_address.line1 DESC
     *
     * @var array
     */
    private $orderBy;
    /**
     * This allows you to set the limit start and limit values by doing:
     *
     * ->limit(10,10)
     *
     * Which would be similar to doing:
     *
     * LIMIT 10, 10
     *
     * @var
     */
    private $limit;
    /**
     * Depth was added in my later en devours to try and help with performance. It allows you to specify the depth at
     * which to retrieve data. Although this helped with optimisation a lot, I really disliked having to use
     * implement this because it seems like a work-around. I would rather be able to increase performance elsewhere so
     * that objects are always retrieved at their full depth
     *
     * @var integer
     */
    private $depth;
    /**
     * This was another newly added feature. Whenever you execute a crud action on a model, the model instance is stored
     * in a local cache if this is true, and/or retrieved from this cached if this value is true.
     *
     * I did find this to make a significant increase on performance, although it did bring in complications that make
     * the system tricky to use at times. You really need to understand how and when to use the cache, otherwise it can
     * be infuriatingly obtuse.
     *
     * @var bool
     */
    private $useCache;
    /**
     * Built into the ORM system, and tied in with the application I set up a webhook system which fires out webhooks on
     * crud events. I discovered the need to be able to disable webhooks at times (when doing large amounts of crud
     * actions in one go) pretty early on. Setting this to false basically disables webhooks on the crud action
     *
     * @var bool
     */
    private $fireWebhooks;
    /**
     * Also build into the application, and tied into the ORM system is an access system. This works on a seperate
     * layer to the database, allowing me to use the same access system as I use for everything in the framework as I do
     * for defining crud action access. However, in some instances I found it useful to disable access checks.
     *
     * This is always on by default. In the api system that I built to access the data models, you were not able to
     * modify this property and therefore were always subject to access checks.
     *
     * @var
     */
    private $ignoreAccessChecks;

    /**
     * The lazy way to create a new instance of options.
     *
     * @return ORMCrudOptions
     */
    public static function n() {
        return new ORMCrudOptions();
    }

    /**
     * Set the query value
     *
     * @param $query
     *
     * @return $this
     */
    public function query($query) {
        $this->query = $query;

        return $this;
    }

    /**
     * Add an orderby field and direction
     *
     * @param $field
     * @param string $direction
     *
     * @return $this
     * @internal param array $orderBy
     *
     */
    public function orderBy($field,$direction="asc") {
        $this->orderBy[] = array($field,$direction);

        return $this;
    }

    /**
     * Set the limit start and limit.
     *
     * @param $limitResults
     * @param null $limitStart
     *
     * @return $this
     */
    public function limit($limitResults,$limitStart=null) {
        $this->limit = array($limitResults,$limitStart);

        return $this;
    }

    /**
     * Set the depth for retrieval
     *
     * @param $depth
     *
     * @return $this
     */
    public function depth($depth) {
        $this->depth = $depth;

        return $this;
    }

    /**
     * Set whether to use the model cache
     *
     * @param $useCache
     *
     * @return $this
     */
    public function useCache($useCache) {
        $this->useCache = $useCache;

        return $this;
    }

    /**
     * Set whether to fire webhooks on crud actions
     *
     * @param $fireWebhooks
     *
     * @return $this
     */
    public function fireWebhooks($fireWebhooks) {
        $this->fireWebhooks = $fireWebhooks;

        return $this;
    }

    /**
     * Set whether to ignore access checks
     *
     * @param $ignoreAccessChecks
     *
     * @return $this
     */
    public function ignoreAccessChecks($ignoreAccessChecks) {
        $this->ignoreAccessChecks = $ignoreAccessChecks;

        return $this;
    }
}

这个类背后的想法是消除在 crud 方法中使用大量参数的需要,并且因为这些参数中的大多数可以在所有 crud 方法中重复使用。请记下查询属性上的注释,因为这一点很重要。


因此,这几乎涵盖了我正在尝试做的事情背后的基本伪代码和想法。最后,我将展示一些用户场景:

<?php
//the most simple way to store a user
$user = User::constructEmpty();
//we use auto incrementing on the id value at the database end. So by not specifying the id, we are not updaing, and
//the id will be auto generated. After the push has been made, the system will assign the id for me
$user->name = "bob";
$user->pass = "bobpass";
//the system automatically constructs child objects for you if they are not yet constructed, because
//it knows what type should be constructed. So I don't need to construct the address object, manually!
$user->address->line1 = "awesome drive";
$user->address->zip = "90051";
//save to storage, but don't fire webhooks and ignore access checks. Note that the ORMCrudOptions object
//is passed to child objects too when recursion happens, meaning that the same options are inherited by child objects
$user->pushThis(ORMCrudOptions::n()->fireWebhooks(false)->ignoreAccessChecks(true));
echo $user->id; //this will display the auto generated id
echo $user->address->id; //this will be the audo generated id of the address object.

//next lets update something within the object
$user->name = "bob updated";
//because we know now that the object has an id value, it will update the existing object. Remembering tha the User
//object is persistent!
$user->pushThis(ORMCrudOptions::n()->fireWebhooks(false)->ignoreAccessChecks(true));
echo $user->id; //this will be the exact same id as before
echo $user->address->id; //this will be a NEW ID! Remember, the address object is NOT persistent meaning that a new
//instance was created in order to ensure that is is infact non-persistent. The system does handle cleaning up of loose
//objects although this is one of the main perforance problems

//finding the above object by user->name
$user = User::findOne(ORMCrudOptions::n()->query("this.name = ('bob')"));
if($user) {
    echo $user->name; //provided that a user with name "bob" exsists, this would output "bob"
}

//finding the above user by address->zip
$user = User::findOne(ORMCrudOptions::n()->query("this.address.zip = ('90051')"));
if($user) {
    echo $user->address->zip; //provided that the user with address->zip "90051" exists, this would output "90051"
}

//removing the above user
$user = User::findById(1); //assuming that the id of the user id 1
//add a favourite book to the user
$user->favouriteBook->name = "awesome book!";
//update
$user->pushThis(ORMCrudOptions::n()->ignoreAccessChecks(true));
//remove
$user->removeThis(ORMCrudOptions::n()->ignoreAccessChecks(true));
//with how persistence works, this will delete the user, and the user's address (because the address is non-persistence)
//but will leave the created book un-deleted, because books are persistent and may exist as child objects to other objects

//finally, constructing from document-oriented
$user = User::constructFromArray(array(
    "user" => "bob",
    "pass" => "passbob",
    "address" => array(
        "line1" => "awesome drive",
        "zip" => "90051"
    )
));
//this will only CONSTRUCT the object based on the internal properties defined property types and object types.
//properties that don't exist in the model's defined properties, but exist in the array will be ignored, so having more
//properties in the array than should be there doesn't matter
$user->pushThis(ORMCrudOptions::n()->ignoreAccessChecks(true));

//update only one property of a user object using arrays (this is ultimately how the api system of the ORM was built)
$user = User::constructFromArray(array(
    "id" => 1,
    "user" => "bob updated"
));
echo $user->pass; //this would output passbob, because the pass was not specified in the array, it was pulled form storage

这里不太可能展示,但是让这个系统使用起来很愉快的原因之一是类文件的生成如何使它们对 IDE 非常友好(特别是对于自动完成)。是的,一些老派开发人员会反对这种新的现代技术,但最终当您处理极其复杂的面向对象数据结构时,让 IDE 帮助您拼写您的属性正确的名称和正确的结构可以挽救生命!

如果您仍然和我在一起,感谢您的阅读。你可能想知道,你又想要什么?.

简而言之,我在文档/对象存储方面没有丰富的经验,在过去的几天里,我已经看到有一些技术可以帮助我实现我正在尝试做的事情。我只是还不能 100% 确定我找到了合适的人。我是否创建一个新的 ORM,我是否可以从现有 ORM 中有效地获取此功能,我是否使用专用的对象/图形数据库?

我非常欢迎任何和所有的建议!


仍然感觉这是一个嵌套集算法,因为您的数据始终适合层次结构。简单类型(字符串、整数等)的层次结构深度为 1,对象表达式如下customer.address.postcode(来自您的相关帖子)每个组件都有一个层次结构级别(在本例中为 3,相应的字符串值存储在最外层的节点中)。

看来这个层次结构可以存储不同的类型,因此您需要对嵌套集算法进行一些小更改。您不是每个节点都携带特定于类(地址、用户等)的列,而是有一个对该类型的字符串引用和一个整数主键来引用它。这意味着您不能对数据库的这一部分使用外键约束,但这是一个很小的代价。 (这样做的原因是单个列不能遵守几个约束之一,它必须遵守所有约束。也就是说,您可能可以使用预插入/预更新触发器做一些聪明的事情)。

因此,如果您要使用 Doctrine 或 Propel NestedSet 行为,您将这样定义表:

  • Node
    • [嵌套设置列,在 ORM 中为您完成]
    • name(varchar,记录元素名称,例如customer)
    • is_persistent (bool)
    • table_name(varchar)
    • primary_key(整数)
  • Address
    • (您常用的列,与任何其他表同上)

现在,我们在这里出现了一个有趣的属性:创建层次结构时,您将看到叶节点中的琐碎值可以通过我们的参考系统进行共享。事实上,我并不完全确定is_persistent布尔值是必需的:由于共享外部表行,它是持久的(如果我正确理解了你的术语),如果不共享,则它是非持久的。

So, if customer1.address.postcode有一个特定的字符串值,你可以得到customer2.address.postcode指向同一件事。当更新第一个表达式指向的版本时,第二个表达式将“自动”更新(因为它解析为同一表行)。

这里的优点是,这将不需要太多工作就可以连接到 Propel 和 Doctrine,并且根本不需要任何核心黑客攻击。您需要做一些工作才能将对象/数组转换为层次结构,但这可能不需要太多代码。


附录:让我更多地解释一下我对嵌套元素存储的想法。你说你相信你需要在不同地方的不同级别共享一个层次结构,但我不太确定(目前我认为你需要一些鼓励,不要建立一个过于复杂的系统!)。让我们看一个例子,用户有一本最喜欢的书。

为了存储它,我们创建这些层次结构:

user
node level 1
points to user record containing id=1, name=bob, pass=bobpass
    favouriteBook
    node level 2
    points to book record containing id=1, name=awesome book
        author
        node level 3
        points to author record containing id=3, name=peter, pass=peterpass

现在,假设我们有另一个用户,想要分享同一作者的另一本最喜欢的书(即我们正在分享user.favouriteBook.author).

user
node level 1
points to different user record containing id=100, name=halfer, pass=halferpass
    favouriteBook
    node level 2
    points to different book record containing id=101, name=textbook
        author
        node level 3
        points to same author record (id = 3)

两个用户共享同一本最喜欢的书怎么样?没问题(我们此外 share user.favouriteBook):

user
node level 1
points to different user record containing id=101, name=donny, pass=donnypass
    favouriteBook
    node level 2
    points to previous book record (id=1)
        author
        node level 3
        points to previous author record (id = 3)

对这种方法可能提出的一个批评是,如果你user.favouriteBook“持久”(即共享)那么它应该共享user.favouriteBook.author自动地。这是因为,如果两个或两个以上的人喜欢同一本书,那么所有人都会看到同一本书的作者。

但是,我在评论中指出了为什么我认为我的显式方法更好:替代方案可能是嵌套集的嵌套集,这可能会变得太复杂,而且到目前为止,我认为您还没有证明您需要它。权衡是我的方法需要更多的存储空间,但我认为这很好。您还需要对对象进行更多设置,但如果您有一个工厂,并且对其进行了可靠的单元测试,我认为您无需担心。

(我认为我的方法也可以更快,但如果不开发两者的原型并测量真实数据集的性能,就很难说)。


附录 2,清理一些评论讨论并将其保留为问题上下文中的答案。

要确定我在此处概述的建议是否可行,您需要创建一个原型。我建议使用现有的嵌套集解决方案,例如带有 NestedSetBehaviour 的 Propel,尽管 GitHub 上还有许多其他库可供您尝试。在此阶段,不要尝试将此原型集成到您自己的 ORM 中,因为集成工作只会分散注意力。目前你想测试这个想法的可行性,仅此而已。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

发现存储特定面向对象数据结构的最佳方法 的相关文章

  • facebook php - 如何获取专辑封面照片

    我需要使用 PHP SDK Facebook 获取专辑封面照片 我尝试 https graph facebook com ALBUM ID picture type album 但我得到默认图像 例如 获取用户 https graph fa
  • 从 octobercms 中的非 ajax 表单获取输入值

    我正在尝试构建一个简单的搜索功能 下面是我的搜索表格
  • PHP 或 WAMP 不确定是什么

    我已经安装了 WAMP 服务器 2 0 PHP 5 4 3 安装WAMP后我已经重新启动了所有服务并且可以打开 phpinfo 显示良好 phpmyadmin 它也显示得很好 我可以使用数据库 然而 当在 Chrome 中运行简单的 php
  • 在 Laravel 5 中截断表

    描述 我有一个充满测试数据的表 有时 我想清除它以获取新数据 我可以在 DBMS 应用程序中执行截断 例如MySQL 工作台 但我试图在我的应用程序中实现它 Goal 创建一个按钮 单击时截断数据库中的表 这是我的步骤 1 声明一条路线 R
  • 为什么验证不起作用并跳转到另一个页面?

    我写了一个customer display php来验证数据 到目前为止只有名字 但无论名字字段是否为空 网页都会跳转到customer search php并且没有更改数据库中的信息 为什么
  • 如何在 PHP 中使用 cURL 发出同时包含 GET 和 POST 参数的请求?

    其他人已经问过如何从 perl java bash 等执行此操作 但我需要在 PHP 中执行此操作 并且我没有看到任何已提出的专门与 PHP 相关的问题 或包含 PHP 的答案 My code ch curl init url curl s
  • 如何在 Visual Studio 中更改 Azure 数据库表的列顺序

    我整个下午都在寻找在 MS Visual Studio 2022 中重新排序 Azure 数据库表列的方法 没有运气 在其他应用程序中 可以通过拖动或剪切和粘贴轻松重新排列列 这里无能为力 此时 我什至不确定可以在 VS 中移动列 我只对
  • 如何解码 eval( gzinflate( base64_decode(

    我已将此代码注入到我的网站中 如何解码尾随字符串 我需要知道发生了什么以及其背后的代码是什么 这应该输出将被执行的代码eval 我希望这就是您正在寻找的
  • WordPress 分页自定义帖子类型

    我有一个名为 新闻 的页面 使用页面模板page newslist php 它应该显示来自自定义帖子类型 也称为新闻 的帖子 我意识到两者具有相同的名称会导致问题 因此在注册自定义帖子类型时 我进行了重写以将其与页面区分开来 rewrite
  • apache_request_headers() 与 $_SERVER

    据我所知 apache request headers 提供与以下相同的信息 SERVER 但按键略有不同 为什么有人应该使用apache request headers 而不仅仅是从那里获取这些信息 SERVER 我在 Centos 上使
  • 使用 PHP 的 MySQL 连接字符串

    我正在尝试通过本地计算机连接到托管在我的服务器上的数据库 我的服务器有cPanel 11 它是一个典型的共享服务器 由CentOS提供支持 安装了PHP和MySQL 准确地说 我在同一台服务器上持有经销商帐户 我想在不同帐户或域之间访问数据
  • Joomla 页面中的自定义 php 代码

    我正在尝试将 Joomla 1 5 9 页面中的表源从页面中的硬编码 html 更改为从 SQL 数据库获取信息 执行此操作的正确方法是什么 创建一个新的模型或组件并以某种方式在页面中使用它 我找到了这些 http docs joomla
  • WordPress 插件中的类自动加载器

    我想编写一个类自动加载器以在 WordPress 插件中使用 该插件将安装在多个站点上 我想尽量减少与其他插件发生冲突的机会 自动加载器将是这样的 function autoload name some code here 我的主要问题是
  • 使用 where 进行 select 语句时,HSQLDB 用户缺乏权限或未找到对象错误

    我的数据库使用 SQuirrel SQL 客户端版本 3 5 3 和 HSQLDB 我已经能够为其指定相应的驱动程序 内存中 并创建一个别名 我创建了一个表 CREATE TABLE ENTRY NAME VARCHAR 100 NOT N
  • Laravel leftJoin 仅右表的最后一条记录

    我是 Laravel 的新手 我有两张桌子 1 产品 2 价格 products id product int p key name varchar prices id price int p key id product int
  • 如何使用 PHP 对字符串进行 rot13 处理?

    我有一个很大的 php 代码 我想手动对其进行编码和解码 我的问题是 php 代码里面有很多单引号和双引号 因此我在使用时出现错误str rot13 功能如下 那么正确的语法是什么以及如何使用下面的函数进行编码 str rot13 That
  • 检查 $_POST 数据

    我正在对表单进行一些垃圾邮件检查 下面的代码在我的本地主机上正常工作 如果为 true 则重定向到 google com 但是 当它在生产服务器上时却不起作用 执行脚本的其余部分并且不重定向到 Google com if POST SERV
  • php curl 使用 GET 发送变量 奇怪的结果

    我正在尝试调用远程站点上页面中的网址 决定使用curl 在远程站点上 url 变量显示为 REQUEST Array var1 gt val1 amp var2 gt val2 amp var3 gt val3 被调用的url是 http
  • 如何从父类中获取子类名

    我试图在不需要子类上的函数的情况下完成此任务 这可能吗 我有一种感觉 但我真的很想确定
  • 将第三个表链接到多对多关联中的桥接表

    设计这个数据库的正确方法是什么 这是我设置表格的方式 我在名为 教师 的表和名为 仪器 的表之间存在多对多关系 然后我有一个连接两者的桥接表 我想将另一个表与 BRIDGE 表关联起来 意思是乐器 老师的组合 该表有 3 行 指定老师可以教

随机推荐