foreach 里面增加数据,键值重复的问题

我快成了一个问题宝宝,但是还是想问,吸收前辈们的经验,想让自己能写出优雅的代码;
那么问题来了,假设我用了数据模型,创建了一个新增方法,然后foreach的时候,键值不自增咋办?
如果把模型初始化放到foreach里面,觉得增加了消耗,每次循环都要new一下;
所以有啥优雅的办法可以解决吗?
我是不是应该把editData写成静态方法?还是怎么样?

通用方法模型 BaseModels,代码:

namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BaseModels extends Model
{
    /**
     * 通用新增、更新数据方法
     * @param array $data 数据
     */
    public function editData($data)
    {
        if (isset($data['_token'])) unset($data['_token']);
        //获取主键
        $pk = $this->primaryKey;
        if (isset($data[$pk]) && $data[$pk]>0) {
            //如果存在主键,则更新数据
            $res = $this->where($pk, $data[$pk])->update($data);
        } else{
            //如果不存在主键,则新增数据
            foreach ($data as $key=>$v) {
                $this->$key = $v;
            }
            $res = $this->save();
        }
        return $this->$pk;
    }
}

Menus 数据模型

namespace App\Models;
use App\Models\BaseModels;
// 继承通用模型
class Menus extends BaseModels
{
    protected $table = 'menus';
}

控制器

namespace App\Http\Controllers;
use App\Models\MenusModels;
class AController extends Controller
{
    public function index()
    {
        $data = [
            [
                'title' => 'a'
            ],
            [
                'title' => 'b'
            ],
            [
                'title' => 'c'
            ],
        ];
        $Menus = new MenusModels();
        foreach($data as $v) {
            // 这么写的话,ID不会自增
            // 可是如果把$Menus = new MenusModels(); 放到foreach里面,是不是就多了消耗?
            // 我想能如何优雅的解决这个问题?
            $Menus->editData($v)
        }
    }
}
附言 1  ·  4年前

editData 是一个新增和更新的通用方法;
意思是传进来的数据如果存在主键则更新,如果没有主键则新增。

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
最佳答案

你这个代码应该只会创建一条数据,后面两次遍历都是在修改第一条创建的数据

public function save(array $options = [])
{
    $query = $this->newModelQuery();

    if ($this->fireModelEvent('saving') === false) {
        return false;
    }

    if ($this->exists) {
        $saved = $this->isDirty() ?
            $this->performUpdate($query) : true;
    }
    else {
        $saved = $this->performInsert($query);

        if (! $this->getConnectionName() &&
            $connection = $query->getConnection()) {
            $this->setConnection($connection->getName());
        }
    }

    if ($saved) {
        $this->finishSave($options);
    }

    return $saved;
}

建议新建模型实例,如果感觉那点性能非常重要,你就这样写:

public function editData($data)
{
    if (isset($data['_token'])) unset($data['_token']);
    //获取主键
    $pk = $this->primaryKey;
    if (isset($data[$pk]) && $data[$pk]>0) {
        //如果存在主键,则更新数据
        $res = $this->where($pk, $data[$pk])->update($data);
    } else{
        //如果不存在主键,则新增数据
        foreach ($data as $key=>$v) {
            $this->$key = $v;
        }
        $this->exists = false; // 强行设置为数据不存在
        unset($this->$pk); // 避免ID 重复
        $res = $this->save();
    }
    return $this->$pk;
}

这样就可以复用模型实例!

4年前 评论
hackxiaoya (楼主) 4年前
GeorgeKing (作者) 4年前
hackxiaoya (楼主) 4年前
讨论数量: 6
 public function index()
    {
        $data = [
            [
                'title' => 'a'
            ],
            [
                'title' => 'b'
            ],
            [
                'title' => 'c'
            ],
        ];
        $menu = new MenusModels();
         $menu->editData($data);
    }
    public function editData(array $data = [])
    {
        $pk = $this->primaryKey;
        foreach ($data as $item) {
            if (isset($item[$pk]) && $item[$pk] > 0) {
                $this->where($pk, $item[$pk])->update($item);
                continue;
            }
            $inserts[] = $item;
        }
        if (isset($inserts)) {
            $this->insert($inserts);
        }
        return true;
    }

如果不想用批量插入,可以了解一下updateOrCreate

4年前 评论
hackxiaoya (楼主) 4年前
hackxiaoya (楼主) 4年前

我试了下,,,,,这个

$times = 10000;

$start = microtime(true);
$config = new Config();
for ($i = 0; $i < $times; $i++) {}

$time1 = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $times; $i++) {
    $config = new Config();
}
$time2 = microtime(true) - $start;

dd($time1, $time2);

执行 1W 次的结果:

file

4年前 评论
hackxiaoya (楼主) 4年前
hackxiaoya (楼主) 4年前
largezhou (作者) 4年前

你这个代码应该只会创建一条数据,后面两次遍历都是在修改第一条创建的数据

public function save(array $options = [])
{
    $query = $this->newModelQuery();

    if ($this->fireModelEvent('saving') === false) {
        return false;
    }

    if ($this->exists) {
        $saved = $this->isDirty() ?
            $this->performUpdate($query) : true;
    }
    else {
        $saved = $this->performInsert($query);

        if (! $this->getConnectionName() &&
            $connection = $query->getConnection()) {
            $this->setConnection($connection->getName());
        }
    }

    if ($saved) {
        $this->finishSave($options);
    }

    return $saved;
}

建议新建模型实例,如果感觉那点性能非常重要,你就这样写:

public function editData($data)
{
    if (isset($data['_token'])) unset($data['_token']);
    //获取主键
    $pk = $this->primaryKey;
    if (isset($data[$pk]) && $data[$pk]>0) {
        //如果存在主键,则更新数据
        $res = $this->where($pk, $data[$pk])->update($data);
    } else{
        //如果不存在主键,则新增数据
        foreach ($data as $key=>$v) {
            $this->$key = $v;
        }
        $this->exists = false; // 强行设置为数据不存在
        unset($this->$pk); // 避免ID 重复
        $res = $this->save();
    }
    return $this->$pk;
}

这样就可以复用模型实例!

4年前 评论
hackxiaoya (楼主) 4年前
GeorgeKing (作者) 4年前
hackxiaoya (楼主) 4年前

1.前端提交数据把新数据的主键设为null,然后遍历数据执行updateOrCreate
2.先全部删除,再批量插入
优雅不优雅不知道,代码量会很少

4年前 评论

@hackxiaoya

public function index()
{
    $data = [
        [
            'title' => 'a'
        ],
        [
            'title' => 'b'
        ],
        [
            'title' => 'c'
        ],
    ];
    $Menus = new MenusModels();
    foreach($data as $v) {
        // Menus 在遍历中始终是同一个模型实例,第一次因为你 new 的,所以 `$Menus->exists` 是 false,但是在第一次调用 editData 方法中的 `$this->save();` 时,会将 `$Menus->exists` 设置为 true
        $Menus->editData($v);
        // 虽然你后面的数据没有定义自增主键,但是模型在第一次创建后,$Menus->exists 就变成了 true 了,这也就意味着,当你再次调用 `$Menus->save();` 时 他并不会 `insert`,而是 `update`!
    }
}
4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!