在Symfony2中将Doctrine与静态YAML数据库一起使用

时间:2013-06-20 20:10:49

标签: php database symfony doctrine-orm yaml

我熟悉PHP,但只是学习Symfony2和Doctrine。我想知道静态数据的最佳实践是什么,就像在将新版本的Web应用程序部署到生产环境时才更新的数据一样。

我更喜欢在YAML中指定静态数据(而不是模式),因为然后修改这些数据对每个人来说都很容易,无论他们是否知道任何PHP / Doctrine。我希望非开发人员能够通过修改.yml文件来添加成就。我想要维护的静态YAML数据库的一个例子是:

Achievements:
  Conservative:
    Difficulty: 2
    Description: >
      Description of Conservative Achievement.
  Dedicated:
    Difficulty: 3
    Description: >
      Description of Dedicated Achievement.
  Persistent:
    Difficulty: 2
    Description: > 
      Description of Persistent Achievement.

现在假设我有一个代表用户的实体

// src/Paulpro/ExperimentingBundle/Entity/User.php
namespace Paulpro\ExperimentingBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class User {

    protected $name;
    protected $achievements;

    public function __construct(){
        // Collection of achievements as defined by achievements.yml
        // $this->achievements = new ArrayCollection();
    }

}

我想将Doctrine正常用于用户,以便它们存储在数据库中,我希望用户能够获得成就。用户可以拥有每个成就的多个,因此在我的用户实体中,我需要某种方式来表示具有数量的成就集合。我不希望成就困难和描述存储在数据库中,只存储在.yml文件中,除非有充分的理由将成就本身存储在数据库中,并且将静态数据导入数据库的好方法自动部署的一部分。

我有三个与此问题相关的主要问题:

  • 有没有更好的方法来做到这一点,请记住,我希望非开发人员能够轻松地添加成就,我可能想要覆盖不同语言环境的achievement.yml文件?

  • 我应该在我的Symfony2包中放置achievement.yml文件吗?

  • 我应该如何修改用户实体,以便生成的数据库可以维持每个用户的成就数量?

3 个答案:

答案 0 :(得分:3)

  

我不希望成就困难和描述   存储在数据库中,仅存在于.yml文件中,除非有好处   有理由将成就本身存储在数据库中并且很好   将静态数据作为自动的一部分导入数据库的方法   部署。

一个很好的理由:管理UsersAchievements之间的关系会更容易 一种将静态数据导入数据库的方法:DoctrineFixturesBundle

1。定义静态配置

最好的方法是expose a semantic configuration

在您的情况下,您将拥有以下2个文件:

// src/Paulpro/ExperimentingBundle/DependencyExtension/Configuration.php

<?php

namespace Paulpro\ExperimentingBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    /**
     * Defines the configuration tree for the bundle
     * 
     * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('paulpro_experimenting');

        $rootNode
            ->children()
                ->arrayNode('Achievements')->addDefaultsIfNotSet()
                    ->children()
                        ->arrayNode('Conservative')->addDefaultsIfNotSet()
                            ->children()
                                ->integerNode('Difficulty')->defaultValue(2)->end()
                                ->scalarNode('Description')->defaultValue('Description of Conservative Achievement.')->end()
                            ->end()
                        ->end()
                        ->arrayNode('Dedicated')->addDefaultsIfNotSet()
                            ->children()
                                ->integerNode('Difficulty')->defaultValue(3)->end()
                                ->scalarNode('Description')->defaultValue('Description of Dedicated Achievement.')->end()
                            ->end()
                        ->end()
                        ->arrayNode('Persistent')->addDefaultsIfNotSet()
                            ->children()
                                ->integerNode('Difficulty')->defaultValue(2)->end()
                                ->scalarNode('Description')->defaultValue('Description of Persistent Achievement.')->end()
                            ->end()
                        ->end();

        return $treeBuilder;
    }
}

<?php

namespace Paulpro\ExperimentingBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class PaulproExperimentingExtension extends Extension
{
    /**
     * Load the configuration for the bundle
     * 
     * @param array $configs
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        foreach($config as $key => $value)
        {
            $container->setParameter('paulpro_experimenting.'.$key, $value);
        }
    }
}

这样,您就可以更好地管理用户使用配置的方式。要查看默认配置结果的示例,可以使用以下命令:

php app/console config:dump-reference PaulProExperimentingBundle

结果如下:

Default configuration for "PaulProExperimentingBundle"
paulpro_experimenting:
    Achievements:
        Conservative:
            Difficulty:           2
            Description:          Description of Conservative Achievement.
        Dedicated:
            Difficulty:           3
            Description:          Description of Dedicated Achievement.
        Persistent:
            Difficulty:           2
            Description:          Description of Persistent Achievement.

这意味着您的用户可以将此示例放在config.yml文件夹下的app\config下,并根据需要进行更改。唯一的条件是,他们在此文件中放置的任何信息都必须由您定义的Configuration树进行验证。

2。定义您的实体

定义Achievement实体,因为它最符合您的需要。例如,你可以使用:

// src/Paulpro/ExperimentingBundle/Entity/Achievement.php
namespace Paulpro\ExperimentingBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class Achievement{

    protected $name;
    protected $difficulty;
    protected $description;    
    /**
     * @ManyToMany(targetEntity="User", mappedBy="achievements")
     * @JoinTable(name="users_achievements")
     **/
    private $users;

    public function __construct() {
        $this->users = new ArrayCollection();
    }

}

除非您必须将关系添加到User,否则您将保持Achievement实体的实现方式:

// src/Paulpro/ExperimentingBundle/Entity/User.php
namespace Paulpro\ExperimentingBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class User {

    protected $name;
    /**
     * @ManyToMany(targetEntity="Achivement", mappedBy="users")
     **/
    protected $achievements;

    public function __construct(){
        $this->achievements = new ArrayCollection();
    }

}

3。部署应用程序时填写数据库

这是最后一步,它只使用DoctrineFixturesBundle

您必须为Achivement实体创建灯具:

// src/Paulpro/ExperimentingBundle/DataFixtures/ORM/LoadAchivementData.php

<?php

namespace Paulpro\ExperimentingBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Paulpro\ExperimentingBundle\Entity\Achivement;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class LoadTypesData implements FixtureInterface, ContainerAwareInterface
{
    private $container;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function load(ObjectManager $manager)
    {
        foreach($this->container->getParameter('paulpro_experimenting.Achievements') as $key => $value)
        {
            $achivement = new Achivement();
            $achivement->setName($key);
            $achivement->setDifficulty($value['Difficulty']);
            $achivement->setDescription($value['Description']);

            $manager->persist($achivement);
        }
        $manager->flush();
    }
}

此灯具将通过paulpro_experimenting.Achievements的配置并从此处加载定义的Achievements 最后,要在数据库中加载数据,您必须运行以下命令:

php app/console doctrine:fixtures:load

此外,您现在应该可以从achievements添加/删除users

答案 1 :(得分:2)

首先,

如果您担心成就的内容或使用的语言,您可以使用 translator组件。基本上它的作用是将您的字符串存储在翻译文件(YAML)中。因此,在您想要添加一些字符串的位置,比如成就名称,您可以添加一些键 achievement.name ,并且翻译器会查找该键的字符串。您可以拥有多个翻译,Symfony会根据客户端的区域设置自动选择正确的翻译。翻译器内置于模板中,因此无需添加任何内容,但您可以在框架的任何位置使用它。

如果你想从文件中获取数据(也许你不仅需要翻译而且还需要一些结构化数据),你可以做一些事情,实际上你并不需要Doctrine。

1)使用config.ini

在每个Symfony2项目中, app /中有一个名为 config.ini 的文件(在更新为 config.yaml 的框架的较新版本上) config / 目录。该文件是存储数据库配置值之类的内容,但您可以根据需要添加任意数量的选项,只需将它们添加到文件中即可:

[parameters]
database_driver="pdo_mysql" 
database_host="192.168.1.1"
...
secret="..."

my_param= "my value"

从控制器调用:

获取这些值很简单
$this->container->getParameter('my_param'); // returns "my value"

它可能适合您,但取决于您要存储的数据类型(对于 ini 文件,只有键/值方式,以及使用的新版本yaml 文件,我只是不确定框架是否会为您解析层次结构(可能会尝试一下))。 需要记住的另一件事是,如果有人删除配置值,您的系统将会关闭(可能是在手动清除缓存后),并且存在我们通常不希望每个人都知道的敏感数据(也就是密码)!

2)使用Symfony2的YAML component

首先以对您有意义的方式创建您的YAML文件,然后将其放在服务器上的某个位置(可能在“资源”或“配置”目录中?) 然后,如果你在控制器上,只需写下:

//This line goes at the begining of your file where all the *use*s are
use Symfony\Component\Yaml\Yaml;

$file = __DIR__.'/../Path/To/file.yml'; //case sensitive on Unix-like systems 
if(!file_exists($file)){
    throw new HttpException(404, "File not found.");
}
//This line does the magic
$index = Yaml::parse(file_get_contents($file));

//Now $index contain an associative array containing all your data.

你已经完成了!我已经用它来加载菜单或在网站上生成新的部分而无需触及代码或数据库,让你的想象力飞翔!

您可以 create a console command 将数据(从您可以想象的任何来源)导入数据库,然后在部署过程中使用该命令作为部署过程的一部分,这取决于您决定是否值得努力。

最后,让我们来谈谈实体关系的事情。

并不是很清楚你想要做什么,但是没有(干净/好的)方法来添加Doctrine实体和随机配置文件之间的关系。问题是它们意味着在应用程序中执行不同的角色。

我建议你这样做:

  1. 在您的数据库中:

    • 使用 id 和名为 nameKey 的列添加表“achievement”,并且可能 descriptionKey ...这些__Key列将包含< strong>翻译键(如前所述)。我:

    “ID”:1,“nameKey”:“achievement.1.name”,“descriptionKey”:“achievement.1.description” “ID”:2,“nameKey”:“achievement.2.name”,“descriptionKey”:“achievement.2.description” “ID”:3,“nameKey”:“achievement.3.name”,“descriptionKey”:“achievement.3.description”

    • 然后修改您的用户,使其链接到成就表。 将它保存在您的数据库中将帮助您并使您能够使用其他一些不错的Symfony工具(如表单,验证器......)并帮助您保持数据的完整性。
  2. 使用翻译

    • 为每个lang创建翻译文件。你有兴趣,他们会看起来像:

    achievement.1.name:“有史以来最好的球员” 成就。描述:“Blah blah blah” achievement.2.name:“比平均水平更好” achievement.2.description:“Blah blah blah”

  3. 只需按照文档查看如何将字符串翻译输出到想要放置的位置。

    要添加新记录,您需要在数据库中添加记录(如果您想更简单,只需为其编写一个小型Web表单)和翻译密钥。

    Symfony非常强大而且容易让你在同一时间变得懒惰和富有成效

答案 2 :(得分:1)

我建议用一堂课代表你的成就。 我还建议使用关系数据库来存储用户和成就之间的关系(逻辑:)) 我的意思是我更愿意将成就存储在数据库中。 使用alice非常简单,你的yaml文件没有那么多改变。

这是一个两步的过程:

  • 修改yaml以满足alice需求
  • 创建一个Achievement实体并添加用户与它之间的关系
Achievement:
  Conservative:
    id: 1
    Difficulty: 2
    Description: >
      Description of Conservative Achievement.    
<?php

class Achievement
{
    public $id;
    public $difficulty;
    public $description;
}

然后执行alice步骤,您就有了一个可行的解决方案。

另一种可能性是将Achievement键作为序列化数组存储在您的用户实体中:

<?php

class User
{
    /** @ORM\Column(type="array") **/
    protected $achievements = array();

    public function addAchievementByName($name)
    {
        $this->achievements[] = $name;
    }

    function getAchievements()
    {
        $self = $this;
        $allAchievements = Yaml::load(__DIR__.'/path/yo/yaml/file');
        return array_flip(array_filter(array_flip($allAchievements), function($achievement) use ($self) {
            return in_array($achievement, $self->achievements);
        }));
    }
}