【问题标题】:PHP Testing JSON Contains with SQLitePHP 测试 JSON 包含 SQLite
【发布时间】:2018-11-05 05:47:56
【问题描述】:

我的 Web 应用使用 Laravel 和 MySQL,但对于我的测试,我在内存中使用 SQLite。

这是我在控制器中使用的代码:

$opportunities = DB::table('opportunities')
        ->whereRaw('JSON_CONTAINS(businesses, \'"' . $business . '"\')')
        ->get();

由于 SQLite 没有 JSON_CONTAINS 函数,因此在测试时会引发异常。我怎样才能解决这个问题,以便我的测试通过并且我不必对结构进行任何大规模更改? SQLite 是否有这样的功能或类似的东西?

谢谢

【问题讨论】:

  • 不要使用 MySQL 的 JSON 特性。坚持 MySQL 和 SQLite 中的标准 SQL 功能。
  • 但是使用不同的 DBMS 进行测试而不是生产确实没有什么意义。如果您的测试成功,您不知道它会在生产环境中工作。

标签: php mysql json laravel sqlite


【解决方案1】:

您可以在测试期间使用sqlite_create_function 模拟JSON_CONTAINS,例如

function json_contains($json, $val) {
  $array = json_decode($json, true);
  // trim double quotes from around the value to match MySQL behaviour
  $val = trim($val, '"');
  // this will work for a single dimension JSON value, if more dimensions
  // something more sophisticated will be required
  // that is left as an exercise for the reader
  return in_array($val, $array);
}

sqlite_create_function(<your db handle>, 'JSON_CONTAINS', 'json_contains');

您可能还想模拟JSON_CONTAINS 的可选第三个参数,例如

function json_contains($json, $val, $path = null) {
  $array = json_decode($json, true);
  // trim double quotes from around the value to match MySQL behaviour
  $val = trim($val, '"');
  // this will work for a single dimension JSON value, if more dimensions
  // something more sophisticated will be required
  // that is left as an exercise for the reader
  if ($path)
    return $array[$path] == $val;
  else
    return in_array($val, $array);
}

【讨论】:

  • 太棒了!!您可能希望将$val = trim($val, '"'); 添加到函数中,以便更好的兼容性,因为 MySQL 会自动剥离它们,而 PHP 不会。
  • @Shiv 好点,我会编辑答案。如果您使用它,也只是注意到第二个版本中有一个错字,我在$array[$path] 比较中使用了= 而不是==
【解决方案2】:

对已接受答案的更多解释:

/**
 * Call this method in your test. 
 * @PHP 7.4+
 */
private function setupSqlite(): void
{
    DB::connection()->getPdo()->sqliteCreateFunction('JSON_CONTAINS', function ($json, $val, $path = null) {
        $array = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
        // trim double quotes from around the value to match MySQL behaviour
        $val = trim($val, '"');
        // this will work for a single dimension JSON value, if more dimensions
        // something more sophisticated will be required
        // that is left as an exercise for the reader
        if ($path) {
            return $array[$path] == $val;
        }

        return in_array($val, $array, true);
    });

然后你可以使用这个查询,它适用于 mysql/mariadb 和 sqlite:

$result = Product::whereRaw('JSON_CONTAINS(tags, \'"' . $tag . '"\')')->get();

在上面的示例中,我使用简单数组搜索 json 列:

$column = ["example", "test", "simple"];

【讨论】:

  • 这是对问题的有用补充。
【解决方案3】:

我放弃了在 SQLite 上运行我的测试套件,因为实现之间存在太多差异。第一个问题是它缺乏对枚举的支持,但是当像 ALTER 语句这样简单的事情导致问题时,我也改用 MySQL 进行测试。

我现在要做的是在phpunit.xml 中指定我的测试数据库的名称。

<phpunit>
    <php>
        <env name="DB_DATABASE" value="mydb_testing"/>
    </php>
</phpunit>

这样做的缺点是需要创建两个数据库来开发应用程序,但我需要确保迁移将在生产环境中工作,生产环境使用 MySQL,而在 SQLite 上的测试并不能证明这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-10
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 2019-05-12
    • 2015-10-04
    • 1970-01-01
    相关资源
    最近更新 更多