草庐IT

php - 在默认 View 之前加载客户端 View (如果存在)

coder 2024-04-15 原文

在我的项目(BtoB 项目)中,我有一个全局应用程序,里面有很多模块。 每个模块都为我的所有客户提供通用功能。

我还在根目录中有一个客户文件夹,在其中,我在他们的文件夹中有所有客户的特殊性。 这些文件夹不是模块。所以他们没有加载 Zf2。我通常使用 abstractFactories 加载这些特性。

这个架构遵循的是我目前拥有的:

-   clients
    -   clientOne
        -   Invoice
        -   Cart
        -   Orders

    -   clientTwo
        -   Invoice
        -   Orders

    -   clientThree
        -   Reporting

-   module
    -   Application
    -   CartModule
    -   InvoiceModule
    -   OrdersModule
    -   Reporting

我的客户想要一些自定义 View ,有时,他们要求我们提供这些 View 。但是我的应用程序为所有这些提供了一个共同的观点。我必须修改此架构以加载客户端 View (如果存在)或加载公共(public) View 。

为了处理这种情况,我想象在每个客户文件夹中都有这个:

-   client
    -   clientOne
        -   Invoice
        -   Cart
            -   View
                - cartView.phtml
        -   Orders

编辑:

在一些好的答案(@AlexP 和@Wilt)之后,我尝试实现这个解决方案:

所以我有一个 ClientStrategy;它的工厂是这样的:

<?php
namespace Application\View\Strategy;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

use Application\View\Resolver\TemplateMapResolver;
use Zend\View\Resolver;

class ClientStrategyFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $viewRenderer = $serviceLocator->get('ViewRenderer');
        $session = new \Zend\Session\Container('Session');

        $map = $serviceLocator->get('config')['view_manager']['template_map'];
        $resolver = new Resolver\AggregateResolver();
        $map = new TemplateMapResolver($map, $this->clientMap($session->offsetGet('cod_entprim')));

        $resolver
            ->attach($map)
            ->attach(new Resolver\RelativeFallbackResolver($map));

        $viewRenderer->setResolver($resolver);

        return new ClientStrategy($viewRenderer);
    }


    /**
     * permet de retourner le namespace du client selectionné avec la relation codpriml / nom de dossier
     * @return array
     */
    public function clientMap($codprim)
    {
        $clients = array(
            21500 => 'clientOne',
            32000 => 'clientTwo',
            // ..
        );

        return (isset($clients[$codprim])) ? $clients[$codprim]: false;
    }
}

我的 clientMap 方法允许我加载我的客户端文件夹,并且它可能包含这样的 View :

class ClientOne
{
    /**
     * get The main Code
     * @return integer
     */
    public function getCodEntPrim()
    {
        return 21500;
    }

    /**
     * Load all customs views
     * @return array
     */
    public function customViews()
    {
        return array(
            'addDotations' => __DIR__ . '/Dotations/view/dotations/dotations/add-dotations.phtml',
        );
    }

    /**
     * GetName 
     * @return string
     */
    public function getName()
    {
        return get_class();
    }
}

因此,当涉及到我的 TemplateMapResolver 来完成他的工作时,我会这样做:

<?php
namespace Application\View\Resolver;

class TemplateMapResolver extends \Zend\View\Resolver\TemplateMapResolver
{
    /**
     * Client name to use when retrieving view.
     *
     * @param  string $clientName
     */
    protected $clientName;

    /**
     * Merge nos vues avec celle clients avant de repeupler l'arrayMap global
     * @param array $map [description]
     */
    public function __construct(array $map, $client)
    {
        $this->setClientName($client);
        if ($this->getCLientName()) {
            $map = $this->mergeMap($map);
        }
        parent::__construct($map);
    }

    /**
     * Merge les map normales avec les map clients, pas propre ?
     * @param  array $map
     * @return array
     */
    public function mergeMap($map)
    {
        $name = $this->getClientName() . '\\' . $this->getClientName() ;
        $class = new $name;
        $clientMap = $class->customViews();
        return array_replace_recursive($map, $clientMap);
    }

    /**
     * Retrieve a template path by name
     *
     * @param  string $name
     * @return false|string
     * @throws Exception\DomainException if no entry exists
     */
    public function get($name)
    {
        return parent::get($name);
    }

    /**
     * Gets the Client name to use when retrieving view.
     *
     * @return string
     */
    public function getClientName()
    {
        return $this->clientName;
    }

    /**
     * Sets the Client name to use when retrieving view.
     *
     * @param mixed $clientName the client name
     *
     * @return self
     */
    public function setClientName($clientName)
    {
        $this->clientName = $clientName;

        return $this;
    }
}

我尝试了很多东西,这很有效,但出现了一些问题:

  • My template_path_stack not works anymore, so a lot of my views are broken.
  • I think this is a complete mess, to do this, that way.
  • Hard to maintain.
  • I understand a bit better, how it works, but i'm still unable to implements it the good way.

最佳答案

如果你真的想这样做(我不太确定这是否是最好的方法)那么你可以用你的自定义逻辑扩展 TemplateMapResolver 并将它设置在你的 Renderer 实例。

创建自定义类:

<?php
Application\View\Resolver

class TemplateMapResolver extends \Zend\View\Resolver\TemplateMapResolver
{
    /**
     * Client name to use when retrieving template.
     *
     * @param  string $clientName
     */
    protected $clientName; 

    /**
     * Retrieve a template path by name
     *
     * @param  string $name
     * @return false|string
     * @throws Exception\DomainException if no entry exists
     */
    public function get($name)
    {
        if ($this->has($clientName . '_' . $name)) {
            return $this->map[$clientName . '_' . $name];
        }
        if (!$this->has($name)) {
            return false;
        }
        return $this->map[$name];
    }
}

现在是这样的:

$resolver = new TemplateMapResolver();
$resolver->setClientName($clientName);

// Get the renderer instance 
$renderer->setResolver($resolver);

您可能仍然需要注意在解析器中设置 map 。也许您可以从旧的解析器中获取它?我不确定……那是你要找出来的。这只是为了让您走上正确的道路。

因此,如果您将 cart_view 设置为模板,它将首先尝试获取 client_name_cart_view,如果未找到,则设置 cart_view

更新

如果您想将其提升到一个新的水平,那么您可以做一个自定义 View 模型,例如扩展普通 ViewModel 类的 ClientViewModel。< br/=""> 此 ClientViewModel 的构造函数同时采用客户端名称和模板名称:

new ClientViewModel($client, $template, $variables, $options);

$variables$options 是可选的,可以传递给 parent::__construct(普通 ViewModel 的构造函数)

下一步是创建一个 Application\View\ClientStrategy

此策略与渲染事件相关联,在此策略中,您添加了一个带有自定义 TemplateMapResolver 集的 ViewRenderer 实例。在呈现期间,您可以从 ViewModel 获取客户端,并使用此客户端在 TemplateMapResolver 中找到正确的模板。

更多的细节可以在网上找到,有例子。例如检查 here .

优点是其他带有 ViewModelJsonModel 的 View 将正常呈现,只有您的 ClientViewModel 得到特殊处理。因此,您不会破坏应用程序的默认逻辑。

关于php - 在默认 View 之前加载客户端 View (如果存在),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34224353/

有关php - 在默认 View 之前加载客户端 View (如果存在)的更多相关文章

  1. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  2. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  5. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  10. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

随机推荐