Как вы работаете с реляционной базой данных в Zend Framework?

Я использую zend framework для написания приложения, и у меня много проблем с работой моих реляционных моделей баз данных. Я прочитал краткое руководство и документы несколько раз, но до сих пор не знаю, как это сделать. Я размещаю изображение взаимосвязей между различными задействованными таблицами, чтобы не объяснять все, поскольку английский не является моим родным языком, и я склонен не объясняться, когда пытаюсь объяснить... ну, сложные вещи. введите здесь описание изображения

Таблицы press_releases, social_networking, blog_posts, rss_feed, directoryios, users и articulos устанавливаются как внешние ключи в таблице planilla_users. Я закодировал свои модели таблиц так, как показано в кратком руководстве:

class Application_Model_DbTable_PlanillaUsers extends Zend_Db_Table_Abstract
{

protected $_name = 'planilla_users';
protected $_referenceMap = array(
    'User' => array(
        'columns' => 'users_id',
        'refTableClass' => 'Application_Model_DbTable_Users',
        'refColumns' => 'id'
    ),
    'Articulo' => array(
        'columns' => 'art_id',
        'refTableClass' => 'Application_Model_DbTable_Articulos',
        'refColumns' => 'id'
    ),...etc

...и остальные в формате:

class Application_Model_DbTable_Users extends Zend_Db_Table_Abstract
{

protected $_name = 'users';
protected $_dependentTables = array('Application_Model_DbTable_PlanillaUsers'); 

У меня также есть модель Planilla.php со всеми сеттерами и геттерами для информации, которая будет храниться/обновляться/извлекаться/удаляться,... однако... я совершенно не знаю, что делать с картографом. Я не знаю, как это должно работать, и я, честно говоря, еще не нашел хорошего примера того, как сделать что-то подобное. Поэтому любая помощь будет в основном оценена.


person la_f0ka    schedule 30.05.2011    source источник
comment
Вы видели эту главу: framework.zend.com/manual/ ru/zend.db.table.relationships.html ? Это из справочного руководства, а не из краткого руководства, и оно должно дать вам все ответы.   -  person Raffael Luthiger    schedule 31.05.2011
comment
@ Раффаэль Лютигер Да, я видел это. Однако я не знаю, как мне заставить работать картограф, когда у меня есть 8 разных таблиц, связанных отношениями «многие к одному». Как, например, выбрать fetchAll()?   -  person la_f0ka    schedule 31.05.2011
comment
Как это связано с вашей предыдущий вопрос? Может быть, удалить этот вопрос, если этот заменит его?   -  person David Weinraub    schedule 31.05.2011
comment
Возможно, взгляните на Как легко создавать модели и отношения между таблицами в Zend Framework. Многое из этого похоже на то, что вы уже определили, но в некоторой степени демонстрирует, как на самом деле использовать определенные отношения.   -  person David Weinraub    schedule 31.05.2011
comment
@David Weinraub, большое спасибо за статью, сейчас проверю. И я удалил другой вопрос, так что спасибо, что обратили внимание   -  person la_f0ka    schedule 31.05.2011


Ответы (1)


Я бы посоветовал вам взглянуть на Doctrine, это объектно-реляционный преобразователь который может довольно хорошо интегрироваться с ZF. Я лично пользуюсь версией 1.2 с ZF 1.11.4 без проблем.

Хороший скриншот здесь, который объясняет, как интегрировать Doctrine в ZF. Доктрина может быть немного сложной для изучения, но как только вы ее поймете, это сэкономит вам время в долгосрочной перспективе. Doctrine также поставляется со сценарием командной строки, который можно использовать для преобразования баз данных в классы Doctrine. Если у вас есть отношения, установленные в базе данных, Doctrine также может их подобрать.

Я также слышал, что в ZF 2 будет включена Doctrine 2.0.

Метод, который я использую для создания классов Doctrine, заключается в том, чтобы сначала настроить доктрину в вашем файле appication.ini и добавить эти строки где-нибудь в заголовке продукта.

[production]

//...

; Doctrine settings
pluginpaths.Freedom_Zend_Application_Resource = "Freedom/Zend/Application/Resource"
resources.doctrine.connection_string = "mysql://username:password@localhost/database_name"
resources.doctrine.compiled = false ; use compiled version of Doctrine
resources.doctrine.cache = false ; use query cache

; Information required for models generator
resources.doctrine.models_path = APPLICATION_PATH "/modules/default/models/Doctrine"
resources.doctrine.module_directories[] = APPLICATION_PATH "/modules/default/models/Doctrine/base"  
resources.doctrine.module_directories[] = APPLICATION_PATH "/modules/default/models/Doctrine"

; Generator settings
resources.doctrine.generate_models_options.phpDocPackage = Your App Name
resources.doctrine.generate_models_options.phpDocSubpackage = Doctrine
resources.doctrine.generate_models_options.phpDocName = Your Company Name
resources.doctrine.generate_models_options.phpDocEmail = [email protected]
resources.doctrine.generate_models_options.pearStyle = true
resources.doctrine.generate_models_options.generateTableClasses = true
resources.doctrine.generate_models_options.generateBaseClasses = true
resources.doctrine.generate_models_options.classPrefix = "Model_Doctrine_"
resources.doctrine.generate_models_options.baseClassPrefix = "Base_"
resources.doctrine.generate_models_options.baseClassesDirectory =
resources.doctrine.generate_models_options.classPrefixFiles = false
resources.doctrine.generate_models_options.generateAccessors = false
//...

Вверху вы заметите строку pluginpaths.Freedom_Zend_Application_Resource Freedom — это мое общее пространство имен в моей библиотеке (см. дерево папок ниже). Здесь у меня есть папка Zend, куда я могу поместить свой дополнительный код ZF, включая переопределение существующих функций ZF, если это необходимо. «Свобода» — название моей компании, ваше, очевидно, будет отличаться. Эта строка должна быть изменена на название вашей компании, например pluginpaths.Yourcompany_Zend_Application_Resource = "Yourcompany/Zend/Application/Resource"

В следующей строке вы размещаете настройки подключения к базе данных resources.doctrine.connection_string = "mysql://username:password@localhost/database_name"

Следующие три строки:

resources.doctrine.models_path = APPLICATION_PATH "/modules/default/models/Doctrine"
resources.doctrine.module_directories[] = APPLICATION_PATH "/modules/default/models/Doctrine/Base"  
resources.doctrine.module_directories[] = APPLICATION_PATH "/modules/default/models/Doctrine"

сообщите Doctrine, где разместить сгенерированные классы, поскольку я использую модульную установку, они входят в мои application/modules/default/models/Doctrine и application/modules/default/models/Doctrine/Base соответственно (см. дерево папок ниже).

Есть некоторые другие строки, которые нужно будет изменить, они должны быть очевидны.

Вам также нужно будет добавить следующие строки под заголовком разработки в файле application.ini.

[development : production]

//...

; phpSettings
resources.doctrine.compiled = false ; use compiled version of Doctrine
resources.doctrine.cache = false ; use query cache

//...

Вам также понадобится файл ресурсов Doctrine.php. Где это находится, зависит от вашей настройки, моя выглядит следующим образом.

-Application
  -configs
  -layouts
  -modules
    -default // ZF default controller
      -controllers
      -models
        -Doctrine // folder for you generated classes
          -Base // Do not change the files in this folder
    -views
      -scripts
-Library
  -Doctrine // the doctrine application as downloaded
   doctrine-cli.php // you will need to create this (see below)
   Doctrine.php // come with the doctrine application
  -Freedom // my generic namespace, shared across multiple apps
    -Zend // place to overide/add ZF classes
      -Application
        -Resource
          *Docrine.php // the file below
  -Zend // ZF 1.11.4 (yours may differ)
  -ZendX // ZF extras

Файл *Doctrine.php выглядит следующим образом (очевидно, игнорируйте *!!)

/**
* Doctrine application resource
*
* @author Juozas Kaziukenas ([email protected])
*/
class Freedom_Zend_Application_Resource_Doctrine extends Zend_Application_Resource_ResourceAbstract
{
    /**
* Initialize
*/
    public function init()
    {
        $doctrineConfig = $this->getOptions();

        if (isset($doctrineConfig['compiled']) && $doctrineConfig['compiled'] == true &&
            file_exists(APPLICATION_PATH . '/../library/Doctrine.compiled.php'))
        {
            require_once 'Doctrine.compiled.php';
        }
        else
        {
            require_once 'Doctrine.php';
        }
        $loader = Zend_Loader_Autoloader::getInstance();
        $loader->pushAutoloader(array('Doctrine_Core', 'autoload'), 'Doctrine');

        $manager = Doctrine_Manager::getInstance();

        // set models to be autoloaded and not included (Doctrine_Core::MODEL_LOADING_AGGRESSIVE)
        $manager->setAttribute(
            Doctrine_Core::ATTR_MODEL_LOADING,
            Doctrine_Core::MODEL_LOADING_CONSERVATIVE
        );

        // enable ModelTable classes to be loaded automatically
        $manager->setAttribute(
            Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES,
            true
        );

        // enable validation on save()
        $manager->setAttribute(
            Doctrine_Core::ATTR_VALIDATE,
            Doctrine_Core::VALIDATE_ALL
        );

        // enable accessor override
        $manager->setAttribute(
            Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE,
            true
        );

        // enable sql callbacks to make SoftDelete and other behaviours work transparently
        $manager->setAttribute(
            Doctrine_Core::ATTR_USE_DQL_CALLBACKS,
            true
        );

        // enable automatic queries resource freeing
        $manager->setAttribute(
            Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS,
            true
        );

        // connect to database
        $manager->openConnection($doctrineConfig['connection_string']);

        // set to utf8
        $manager->connection()->setCharset('utf8');

        if (isset($doctrineConfig['cache']) && $doctrineConfig['cache'] == true)
        {
            $cacheDriver = new Doctrine_Cache_Apc();

            $manager->setAttribute(
                Doctrine_Core::ATTR_QUERY_CACHE,
                $cacheDriver
            );
        }

        return $manager;
    }
}

Затем вам нужно будет создать файл doctrine-cli.php для начальной загрузки вашего приложения, он должен быть расположен в корне файла вашей библиотеки (см. дерево выше), мой выглядит следующим образом.

/**
 * Doctrine CLI script
 *
 * @author Juozas Kaziukenas ([email protected])
 */

define('APPLICATION_ENV', 'development');
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../your/application'));

set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    './',
    get_include_path(),
)));

require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

$application->getBootstrap()
        ->bootstrap('doctrine')
        ->bootstrap('autoload');

// set aggressive loading to make sure migrations are working
Doctrine_Manager::getInstance()->setAttribute(
    Doctrine::ATTR_MODEL_LOADING,
    Doctrine_Core::MODEL_LOADING_AGGRESSIVE
);

$options = $application->getBootstrap()->getOptions();

$cli = new Doctrine_Cli($options['resources']['doctrine']);

$cli->run($_SERVER['argv']);

Единственная строка, которую вам нужно изменить, это

define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../your/application'));

поэтому он указывает на корневую папку вашего приложения, аналогично строке в вашем файле index.php в вашей общей папке.

Теперь, надеюсь, вы должны быть готовы сгенерировать файлы классов вашей базы данных. Перейдите в окно терминала и введите следующее.

cd /home/path/to/library
php doctrine-cli.php generate-models-db

Если все прошло хорошо, папки application/modules/default/models/Doctrine и application/modules/default/models/Doctrine/Base должны содержать классы для вашей базы данных. Как я упоминал выше, не изменяйте файлы в папке Base, вы можете использовать классы в родительской папке Doctrine для внесения изменений. Вы также заметите, что в папке Doctrine есть два класса для каждой таблицы базы данных, один из которых имеет суффикс Table. Здесь я размещаю свой код DQL, чтобы держать его подальше от моих моделей/контроллеров. Другие классы можно использовать для внесения изменений в классы в базовой папке, также вы можете добавить сюда хуки и прослушиватели, чтобы добавить специфичный для таблицы код, например, добавить шифрование пароля или предустановить даты и т. д.

Я надеюсь, что объяснил это достаточно ясно, так как это тяжелая работа, но так устроена моя.

Надеюсь, это поможет.

person Garry    schedule 31.05.2011
comment
Что ж, @Гарри, спасибо за этот ответ. Я слышал о Doctrine только хорошие вещи, но, поскольку сейчас я только подхожу к самой Zend Framework, боюсь, что выбор Doctrine может оказаться для меня слишком суровым... по крайней мере, на данном этапе. Тем не менее, я проверю скриншот, прежде чем принять решение об этом. Возможно, это будет не так сложно, как я себе сейчас представляю. - person la_f0ka; 31.05.2011
comment
Я следую учебнику zend cast по интеграции ZF/Doctrine прямо сейчас. И я также ищу информацию о том, как генерировать классы для моих существующих таблиц БД... (поскольку я думаю, что выполнение этого вручную может быть немного... подвержено ошибкам.) Кажется, я ничего не нашел ... вы сделали или читали о том, как это сделать? - person la_f0ka; 31.05.2011
comment
@la_f0ka Извините за задержку с ответом. Я изменил вышеизложенное, чтобы, надеюсь, вы начали работать с Doctrine. Кое-что из этого по памяти, поэтому я надеюсь, что ничего не пропустил. Пожалуйста, дайте мне знать, как вы поживаете - person Garry; 01.06.2011
comment
@Гарри большое спасибо за помощь. Я (вроде как) уже получил модели для работы с 'orm:convert-mapping --from-database yml ~/' и 'orm:generate-entities ~/', однако этим моделям не хватает много информации (они даже не установлены пространства имен). Однако я следовал вашим инструкциям, потому что это казалось чертовски проще, чем то, что я сделал. при выполнении 'generate-models-db' я получил эту ошибку: - person la_f0ka; 01.06.2011
comment
'Предупреждение PHP: call_user_func() ожидает, что параметр 1 будет допустимым обратным вызовом, класс 'Doctrine_Core' не найден в /home/fiodorovich/library/ZendFramework/library/Zend/Loader/Autoloader.php в строке 124', & 'PHP Fatal ошибка: класс «Doctrine_Manager» не найден в /home/fiodorovich/public_html/gisele/library/Federico/Zend/Application/Resource/Doctrine.php в строке 28 '... Любые идеи о том, что может быть причиной этого? - person la_f0ka; 01.06.2011
comment
@ la_f0ka У вас есть настройка пространства имен Doctrine? В вашем application.ini autoloadernamespaces.doctrine = Doctrine_ - person Garry; 01.06.2011
comment
@Garry Да, на самом деле он был загружен как Doctrine без подчеркивания. Я только что добавил вашу версию и получил пару дополнительных сообщений об ошибках. 'Предупреждение: include_once(Doctrine/Core.php) [function.include-once]: не удалось открыть поток: нет такого файла или каталога в /home/fiodorovich/library/ZendFramework/library/Zend/Loader.php в строке 146' . Я не знаю, связано ли это, но я не могу найти файл doctrine.compile.php - person la_f0ka; 01.06.2011
comment
@ la_f0ka Я никогда раньше не сталкивался с файлом doctrine.compile.php. Установлен ли Doctrine в той же папке, что и Zend? Похоже, автозагрузчик ZF не может его найти. Папка Doctrine должна быть включена в ваш путь включения, как указано в index.php и в doctrine-cli.php. - person Garry; 01.06.2011
comment
@Garry, папка Doctrine находится внутри библиотеки, которая сама установлена ​​​​в файле index.php. ... Zend, однако, там нет... он находится во внешней библиотеке: '/home/fiodorovich/library/ZendFramework/library/Zend' - person la_f0ka; 01.06.2011
comment
@la_f0ka la_f0ka Хммм, странно, почему ZF не может загрузить файл Doctrine/Core.php. Я предполагаю, что папка Doctrine имеет заглавную букву «D» и содержит файл Core.php. - person Garry; 01.06.2011
comment
@Garry На самом деле в папке Doctrine есть папки Common, DBAL и ORM. Я использую Doctrine 2... и на самом деле у меня также есть доктрина, установленная с PEAR... что может каким-то образом с этим столкнуться. - person la_f0ka; 01.06.2011
comment
@ la_f0ka Может быть, я использую Doctrine 1.2.4. Насколько я понимаю, Doctrine 2 — это другой котелок с рыбой. Я не знаю, как интегрировать Doctrine 2 в ZF, но слышал, что это можно сделать. Я не планирую переходить на Docrine 2, пока не выйдет ZF2. Вы можете установить 1.2.4 и попробовать еще раз, возможно, это самое простое решение для вас. Загрузка доктрины - person Garry; 01.06.2011
comment
@Гарри, да, это было все. Скачал старую добрую доктрину и все заработало отлично. Большое спасибо за помощь, Гарри. Могу я спросить вас, в чем разница между моделями в modules/default/models/Doctrine и моделями в modules/default/models/Doctrine/Base?? просто чтобы знать, что искать дальше и, наконец, начать работать с Doctrine/ZF?? - person la_f0ka; 01.06.2011
comment
Файлы базовых папок будут перезаписываться каждый раз, когда вы запускаете php doctrine-seevee-cli.php generate-models-db, поэтому любые внесенные вами изменения будут перезаписаны. Файлы в родительской папке записываются только при первом запуске. Файлы с тем же именем в папке Doctrine можно использовать для переопределения настроек в файлах базовых папок. Например, вы можете легко изменить, добавить или удалить отношения в этих файлах. - person Garry; 01.06.2011
comment
хорошо @Garry, большое спасибо, что нашли время, чтобы объяснить мне все это. Я знаю, что иногда могу быть упрямым, поэтому я очень ценю вашу помощь. Вы были гораздо более полезными, чем кто-либо в #doctrine и #zftalk ;-) - person la_f0ka; 01.06.2011
comment
Нет проблем @la_f0ka рада, что смогла чем-то помочь, также добро пожаловать в сообщество Doctrine - person Garry; 01.06.2011
comment
@Garry Прости, но у меня поврежден мозг, как создать объект из моделей внутри папки modules/default/models/Doctrine? Или я должен как-то использовать это для создания экземпляра Doctrine? - person la_f0ka; 01.06.2011
comment
нужно ли где-то объявить, что вы используете модели из /modules/default/models/Doctrine? Потому что я получаю исключение с этим Fatal error: Class 'Model_Doctrine_PlanillaUsersTable' not found in /home/fiodorovich/public_html/gisele/application/controllers/IndexController.php on line 18 - person la_f0ka; 02.06.2011
comment
Нет, в моей настройке нет ничего, чтобы указать модели в доктрине. ZF должен иметь возможность автоматически загружать их. Можете ли вы загрузить другие модели в порядке? - person Garry; 02.06.2011
comment
@Garry Я могу загрузить те, которые находятся в каталоге application/models. Вот почему я спросил - person la_f0ka; 02.06.2011
comment
@ la_f0ka Я так понимаю, эта строка вызывает ошибку $this->_candidateTable = Model_Doctrine_PlanillaUsersTable::getInstance();? - person Garry; 02.06.2011
comment
@Garry Я на самом деле тестирую это с контроллера индекса, вот так $u = new Model_Doctrine_Base_Pais(); //base or not base gives me the same error, а затем я var_dump это. - person la_f0ka; 02.06.2011
comment
@la_f0ka Извините, мне пора идти, я подумаю об этом утром. - person Garry; 02.06.2011
comment
@Гарри, не волнуйся. Я буду продолжать пытаться и дам вам знать здесь, если я нашел решение. Вероятно, мне просто нужно где-то объявить, что я буду использовать модули или что-то в этом роде. - person la_f0ka; 02.06.2011
comment
@ Гарри, ... я наконец-то заработал! Проблема заключалась в том, что ранее у меня была жестко запрограммированная модель в качестве типа ресурса в начальной загрузке, указывающей на папку application/models. Я изменил это на подпапку модулей, и это сработало! Я думаю, вы можете загружать модели только из одного места в ZF. - person la_f0ka; 02.06.2011