3

我熟悉 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 文件中,除非有充分的理由将成就本身存储在数据库中,以及将静态数据导入数据库的好方法自动部署的一部分。

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

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

  • 我应该在 Symfony2 包中的哪个位置放置成就.yml 文件?

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

4

3 回答 3

3

我不希望将成就的困难和描述存储在数据库中,只存储在 .yml 文件中,除非有充分的理由将成就本身存储在数据库中,以及将静态数据导入数据库的好方法自动部署的一部分。

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

1.定义你的静态配置

最好的方法是公开语义配置

在您的情况下,您将拥有以下 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.

于 2013-07-02T15:01:04.687 回答
2

第一件事,

如果您关心成就的内容或使用的语言,您可以使用翻译组件。基本上它所做的是将您的字符串存储在翻译文件(YAML)中。因此,在您要添加一些字符串的地方,说出您添加一些关键成就名称的成就名称,然后翻译器会查找该关键的字符串。您可以进行多种翻译,Symfony 会根据客户端的语言环境自动选择正确的翻译。翻译器内置于模板中,因此无需添加任何内容,但您也可以在框架的任何位置使用它。

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

1) 使用 config.ini

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

[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 组件

首先以对您有意义的任何方式创建您的 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.

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

您可以创建一个控制台命令,将数据(从您可以想象的任何来源)导入数据库,然后在某些脚本中使用该命令作为部署过程的一部分,由您决定是否值得付出努力。

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

尚不清楚您要做什么,但是没有(干净/好的)方法可以在 Doctrine 实体和随机配置文件之间添加关系。问题是它们旨在在应用程序中扮演不同的角色。

我建议你做这样的事情:

  1. 在您的数据库上:

    • 添加一个带有id的表“成就”和一个名为nameKey的列,也许还有descriptionKey ...这些 __Key 列将包含翻译器键(如前所述)。IE:

    “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. 使用翻译器

    • 为每个语言创建一个翻译文件。你有兴趣,它们看起来像:

    成就.1.name:“有史以来最好的球员”成就.1.description:“Blah blah blah”成就.2.name:“优于平均水平”成就.2.description:“Blah blah blah”

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

要添加新记录,您需要在数据库中添加一条记录(如果您想让它更容易,只需为此编写一个小型 Web 表单)和翻译键。

Symfony 功能强大而简单,让你既懒惰又高效

于 2013-06-28T01:36:50.467 回答
1

我建议使用一个类来代表您的成就。我还建议使用关系数据库来存储用户和成就之间的关系(逻辑:))我的意思是我更愿意将成就存储在数据库中。使用alice非常简单,而且您的 yaml 文件没有太多需要更改的地方。

这是一个两步过程:

  • 修改 yaml 以适应爱丽丝的需要
  • 创建一个成就实体并在用户和它之间添加关系
Achievement:
  Conservative:
    id: 1
    Difficulty: 2
    Description: >
      Description of Conservative Achievement.    
<?php

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

然后按照爱丽丝的步骤,你就有了一个可行的解决方案。

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

<?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);
        }));
    }
}
于 2013-06-29T10:28:09.357 回答