基于底层数据的动态计算

时间:2014-08-16 19:40:41

标签: php jquery mysql ajax symfony

故事。

我正在尝试根据多种不同因素计算产品成本。我的系统目前使用PHP函数完美运行,但我想添加一些Ajax以产生更友好的用户体验。

我目前是怎么做的。

实体

//entity/ProductRecipe.php
public function productcost2amountcost() {
$this->productcost = null; 
$am = $this->amount;
$cu = $this->product->getCostunit();
$productcost = $am * $cu;
$this->productcost = $productcost;
$this->recipe->fixRecipecost();
$this->recipe->fixCostperyield();
}


//entity/Recipe.php
public function fixRecipecost() {
$this->recipecost = 0;
foreach ($this->product AS $pc) {
$this->recipecost += $pc->getProductcost();
$this->setRecipecost($this->recipecost);
}
}

public function fixCostperyield(){
$this->costperyield = null;
$cy = $this->recipeyield;
$rc = $this->recipecost;
$this->costperyield = $rc / $cy;
}

表单

//Form/RecipeType.php

$builder
->add('recipename', 'text', array(
'label' => 'Recipe Name'))
->add('recipeyield', 'number', array(
'label' => 'Recipe Yield'))
->add('product', 'collection', array(
'label' => 'Ingredients',
'type' => new ProductRecipeType(),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
));

//Form/ProductRecipeType.php

$builder 
->add('product', 'entity', array(
'class' => 'BCInventoryBundle:Product',
'property' => 'prodlist',
))

->add('amount', 'number', array(
'label'=>'Quantity',
)) 
->add('measure', 'entity', array(
'class' => 'BCInventoryBundle:Measures',
'property' => 'unit',
))
->add('productcost' ,'money', array(
'currency' => false,
'read_only' => 'true',        
))      
;

正如我之前所说,一切都很好,虽然有点无聊和静止。

Image showing example of use case

问题

从图片中可以看出。 ProductRecipe用作Recipe表单中的表单集合。我想要的是,一旦用户从数据库(Butter)中选择了一个产品并说明了数量(1)和度量(kg)我需要Ajax才能首先获得UnitCost(所有单位都转换为Grams并更新名为Unitcost的文件) )

1kg转换为g = 1000,1000 * unitcost(0.0079600000)= £7.96 < - 需要将其放入表单的ProductCost字段中。

任何帮助都会受到极大的赞赏,即使正确方向上的一点也会令人惊叹。我花了几个小时谷歌,但出现的东西似乎永远不是我需要的,特别是涉及Symfony2时。

如何使用Ajax运行productcost2amountcost()函数,以便在没有页面刷新的情况下填充ProductCost字段。

提前谢谢你。道格。

答案

感谢Santiag00经过多次试验和错误后,我们都得到了它的工作。他已经更新了他的部分,但我想详细说明一下。

的Javascript

//Calc.js
$(document).on('change', '.products, .amounts, .unit', function(event) {
var amount = $(this).parent().parent().parent().find('.amounts').val();
var productId = $(this).parent().parent().parent().find('.products').val();
var unit = $(this).parent().parent().parent().find('.unit').val();
var productCostField = $(this).parent().parent().parent().find('.product-costs');

//The above assign a Var to each of the field's needed for the JS

console.log(productCostField);

console.log("Amount: " + amount + " - ProductID: " + productId + " - unit: " + unit);
if (amount == "" || productId == "" || unit == "") {
// Don't make the Ajax call if you are missing one of the two values
return false;
}

// This will be triggered every time a product or amount input field is changed
$.post(
Routing.generate('calculate_cost'), 
//This line is what connects to the Function in the controller and defined in routing.yml. Made easier by     
//https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/blob/master/Resources/doc/index.md
{
// Use the corresponding amount and product ID 
product: productId, amount: amount, unit: unit,
},
function(data) {
data = JSON.parse(data);
if (!data.success) {
// An error was thrown in the controller
alert(data.message);
}
else {
// Update the corresponding productCost field using the data from the controller
console.log("Product cost: " + data.productCost);
productCostField.val(data.productCost);

}
}
);
}
);

从上面的JS。

调用的路线
//routing.yml
calculate_cost:
pattern: /productcost
defaults: { _controller: "BCInventoryBundle:ProductRecipe:getProductCost" }
options:
expose: true

最后,从上面的JS。

调用的函数
//ProductRecipeController.php
public function getProductCostAction(Request $request) {

$amount = $request->request->get('amount', null);
$productId = $request->request->get('product', null);
$unit = $request->request->get('unit', null);
if (empty($amount) || empty($productId)) {
return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => false, 'message' => 'Bad input')));
}

$em = $this->getDoctrine()->getManager();

$product = $em->getRepository('MyBundle:Product')->find($productId);
$u = $em->getRepository('MyBundle:Measures')->find($unit);

$mass = new Mass($amount, $u->getUnit());
$fam = $mass->toUnit('g');

if (empty($product)) {
return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => false, 'message' => 'Invalid product')));
}
$productCost = $product->getCostunit() * $fam;
return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => true, 'productCost' => $productCost)));
}

我真的希望这对其他人有帮助。如果你发现它很有用,那么我们花了很多时间试图解决这个问题。要注意的主要部分是我们如何选择字段,因为它们在Symfony中使用嵌入表单时的嵌套方式。

2 个答案:

答案 0 :(得分:1)

productcost2amountcost看起来太复杂了,它与模型的状态有很强的关系。

如果您想在每次客户端发送Ajax请求时更新DB(或某些商店),您可以使用productcost2amountcost。但它既昂贵又有风险。 (您必须控制请求的顺序)[Solution1]

如果您想更简单地处理请求,我认为您应该将productcost2amountcost转换为无状态(作为过程)和一些小逻辑。 (由客户管理的国家)[解决方案2]
新程序通过ajax接收一些参数(例如产品,数量,量度),并发送响应(例如productcost) (如果你使用临时模型(非存储),你可以使用productcost2amountcost。但你应该从productcost2amountcost删除对食谱的引用)

但在这种情况下,我认为你不必使用ajax。 您可以通过javascript计算所有成本与表单数据(包括隐藏单位成本和度量标度)的可用性,并在服务器端重新计算以便最后更新。 [Solution3]
不同架构的逻辑复制和计算都很糟糕,但它可能是简单的解决方案。

答案 1 :(得分:1)

一种解决方案是安装“FOSJsRoutingBundle”(https://github.com/FriendsOfSymfony/FOSJsRoutingBundle)以在Javascript中公开路线。这样,您就可以在Controller中创建一个新操作,用于计算产品成本并将其作为JSON返回给HTML。

控制器中的操作可能如下所示:

/**
* @Route("/productcost", name="calculate_cost", options={"expose"=true})
* @Method("POST")
*/
public function getProductCostAction(Request $request) {
    $amount = $request->request->get('amount', null);
    $productId = $request->request->get('product', null);
    if (empty($amount) || empty($productId)) {
        return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => false, 'message' => 'Bad input')));
    }
    $product = $this->getDoctrine()->getManager()->getRepository('ProductBundle:ProductRecipe')->findOneBy(array('id' => $productId));
    if (empty($product)) {
        return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => false, 'message' => 'Invalid product')));
    }
    $productCost = $product->getCostunit() * $amount;
    return new \Symfony\Component\HttpFoundation\Response(json_encode(array('success' => true, 'productCost' => $productCost)));
}

然后AJAX调用看起来像这样:

<script>
    $(document).on('change', '.products, .amounts', function(event) {
        var amount = $(this).parent().children('.amounts').val();
        var productId = $(this).parent().children('.products').val();
        if (amount == "" || productId == "") {
            // Don't make the Ajax call if you are missing one of the two values
            return false;
        }

        // This will be triggered every time a product or amount input field is changed
        $.post(
            Routing.generate('calculate_cost'), 
            { 
                // Use the corresponding amount and product ID 
                amount: amount,
                product: productId
            },
            function(data) {
                data = JSON.parse(data);
                if (!data.success) {
                    // An error was thrown in the controller
                    alert(data.message);
                }
                else {
                    // Update the corresponding productCost field using the data from the controller
                    $(this).parent().children('.product-costs').val(data.productCost);
                }
            }
        );
    });
</script>