【问题标题】:How do you delete a to do from a list that is reversed?如何从反转的列表中删除待办事项?
【发布时间】:2018-03-26 02:04:25
【问题描述】:

我使用 array_reverse 将新创建的待办事项添加到列表顶部。但现在我无法删除它们。当我单击复选框删除项目时,它将删除列表另一端的项目。谁能告诉我颠倒顺序并让它们正确删除的最佳方法? array_reverse 是完成此任务的最佳方法还是有更好的选择。我的两个文件在下面。

index.php

<?php
// cookies
ini_set('session.gc_maxlifetime', 172800);

session_set_cookie_params(172800);

// array('get milk', 'feed dog');
include 'TaskList.php';


$list = new TaskList('tasks');


if(isset($_POST['task'])) {
  // if post task is set the user submitted a task
  $list->addItem($_POST['task']);
  $list->save();
}

if(isset($_POST['delete'])) {
  // delete this item from the array
  $keys = $_POST['delete'];
  foreach($keys as $key) {
      array_reverse($list->deleteItem($key));
  }
  $list->save();
 }


?>

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>To Do List</title>
  </head>
<body>

<h1>To Do List</h1>

<!-- Insert Items -->
<form action="index.php" method="post">
  <label for="task">
    Enter task: <input type="text" name="task" id="task" value="" placeholder="Enter Task Here">
  </label>
  <input type="submit" name="" value="Add">
</form>

<form class="" action="index.php" method="post">
  <ul>
    <?php foreach(array_reverse($list->items) as $key => $task): ?>
      <!-- [] tell browser that it's an array and to delete more than one item at a time -->
      <li><input type="checkbox" name="delete[]" value="<?php echo $key ?>"><?php echo $task; ?></li>
    <?php endforeach; ?>
  </ul>
  <input type="submit" name="" value="Delete">
</form>

<hr>

<?php var_dump(array_reverse($list->items)) ?>

</body>
</html>

任务列表.php

<?php


class TaskList {

  var $items;
  var $listname;

  public function __construct($listname){
    session_start();

    $this->listname = $listname;

    if(isset($_SESSION[$this->listname])) {
      $this->items = $_SESSION[$this->listname];
    }
    else {
      $this->items = array();
    }
  }

  public function addItem($item) {
    $this->items[] = $item;
  }

  public function deleteItem($key) {
    unset($this->items[$key]);
  }

  public function save() {
    $_SESSION[$this->listname] = $this->items;
  }

}

【问题讨论】:

  • 你不应该改变(并不断改变)你的数据结构只是为了以不同的方式显示它。这只是令人困惑。如果您希望它反向显示,您可以使用 for() 循环并向后退。这将保留您的键/值对。
  • 如果你死心塌地使用array_reverse(),你应该告诉它保留密钥(参见:php.net/manual/en/function.array-reverse.php
  • 我必须承认,我有些困惑:为什么直接通过key访问要删除的元素时需要修改列表的顺序?
  • 另外,你能澄清一下“当我单击复选框删除一个项目时,它会删除列表另一端的那个”是什么意思?
  • 好的,感谢您的澄清。我只希望它的显示方式被反转,所以添加的项目会放在列表的顶部而不是底部。我所说的“删除对面”的意思是说,当我检查列表最底部的项目并单击删除时,它会删除顶部的项目。

标签: php arrays oop


【解决方案1】:

反向删除

这里的目标是删除数组中对面的元素。一种方法是使用 array_keys 获取数组的键,然后在结果上使用 array_reverse 为我们提供相反键的映射。

$reverseKeys = array_reverse(array_keys($arr));

// output of print_r($reverseKeys) for an array indexed from 0 to 4
Array
(
    [0] => 4
    [1] => 3
    [2] => 2
    [3] => 1
    [4] => 0
)

那么我们就可以使用这个map来删除相应的元素了。

if(isset($_POST['delete'])) {
    // delete this item from the array
    $keys = $_POST['delete'];
    $reverseKeys = array_reverse(array_keys($arr));

    foreach($keys as $key) {
        $list->deleteItem($reverseKeys[$key]);
    }
    $list->save();
}

由于这个问题有oop 标签,我将尝试专注于提供更面向对象的方法以及如何改进当前的设计。

TaskList 对象的设计

首先,让我们看看TaskList 对象的设计,看看我们可以改进什么。

封装

面向对象编程的一个关键概念(对某些人来说最重要)是encapsulation。简而言之,封装意味着对象的内部工作原理和属性对使用该对象的代码是隐藏的。这实际上意味着一个对象应该具有private 属性,并且只能通过公共访问器公开它们(部分或全部)。

访问器可以返回一个没有修改的值

public function getDateOfBirth() {
    return $this->dateOfBirth;
}

或进行一些修改以赋予数据更多意义

public function getAge() {
    // assuming $dateOfBirth is an instance of DateTime
    $diff = $dateOfBirth->diff(new DateTime());
    return $diff->format('%y');
}

查看TaskList类,我们可以将属性$items$listName转换为私有成员,并为它们创建访问器。

class TaskList {

    private $items = [];
    private $listname = '';

    public function __construct($listname){
        /* ... */
    }

    public function getListName() {
        return $this->listName;
    }

    public function getItems() {
        return $this->items;
    }
}

我觉得班上的其他人都不错; addItem()deleteItem()save() 都非常不言自明并且包含得很好。但是其余的代码呢?查看index.php 文件,有些东西我们可以重构为TaskList。最大的一个是执行批量删除的foreach 循环。 TaskList 类应该能够自行执行此操作,而不会将其内部结构暴露给其余代码。

// rename delete to deleteOne
public function deleteOne($key) {
    unset $this->items[$key];
}

public function deleteItems($keys) {
    foreach($keys as $key) {
        $this->deleteOne($key);
    }
}

// reverse delete from earlier
public function reverseDeleteItems($keys) {
    $reverseKeys = array_reverse(array_keys($arr));

    foreach($keys as $key) {
        $this->deleteOne($reverseKeys[$key]);
    }
}

责任

另一个经常应用在面向对象编程中的重要原则是Single Responsability。单一职责原则 (SRP) 无需过多详细说明,它指出对象应仅处理其自己的数据/进程,而不能超出该范围。在这里,TaskList 的职责是维护一个任务列表。但是,它也在其构造函数中处理 php 会话,这超出了它的职责范围。

index.php:

<?php
    session_start()
    /* ... */

任务列表.php:

class TaskList {
    /* ... */
    public function __construct($name) {
        $this->listname = $name;

        if(isset($_SESSION[$this->listname])) {
            $this->items = $_SESSION[$this->listname];
        }
        else {
            $this->items = array();
        }
    }

进一步改进

上面应该有足够的信息来帮助您解决问题,但我想借此机会提供一些建议。

首先,$items 是一个简单的数组,现在运行良好,但将来可能会导致问题。一个数组实际上可以包含任何东西,而且它的格式可能不正确。你可以做的是创建一个Task 类,并让Item 的每个元素实例化它。这样一来,验证每个任务是否具有适当的元素就变得非常容易,并且可以更好地扩展。

class Task {

    private $name;
    private $value;
    private $priority;  // value from 0 to 9
    private $createdAt;
    private $updatedAt;

    public function __construct($name, $value, $priority = 5) {
        $this->name = $name;
        $this->value = $value;
        $this->priority = $priority;

        $now = new DateTime();
        $this->createdAt = $now;
        $this->updatedAt = $now;
    }

    public function getName() {
        return $this->name;
    }

    public function getValue() {
        return $this->value;
    }

    public function setValue($value) {
        $this->value = $value;
        $this->updatedAt = new DateTime();
    }

    public function getPriority() {
        return $this->priority;
    }

    public function toArray() {
        return [
            'name' => $this->name,
            'value' => $this->value,
            'priority' => $this->priority,
            'createdAt' => $this->createdAt->format('Y-m-d H:i:s'),
            'updatedAt' => $this->updatedAt->format('Y-m-d H:i:s'),
        ];
    }
}

这使您可以像这样修改add 函数

public function addItem($name, $value, $priority = null) {
    // if items must be unique
    if(array_key_exists($name, $this->items)) {
        throw new Exception('An item with the same name already exists');
    }

    $this->items[$name] = new Task($name, $value, $priority);
}

如您所见,我现在只需对 TaskList 类进行少量修改即可获得有关每个 Task 的更多信息。

此外,您可以通过重构 TaskListRepository 中的保存机制来进一步改进 TaskList,该机制将负责获取 TaskList 的实例并将它们持久保存到您喜欢的任何系统(MySQL、SQLite、MongoDB、. txt 文件,会话变量)。

class TaskListRepository {

    public function save($list) {
        /* store list */
    }

    public function getOne($name) {
        /* fetch instance of a TaskList */
        return $taskList;
    }
}

您现在可以像这样修改index.php

<?php
    session_start();

    $taskListRepo = new TaskListRepository();
    $taskList = $taskListRepo->getOne('myList');

    $taskList->addOne('groceries', 'I need to pick up the groceries');
    $taskListRepo->save($taskList);

【讨论】:

    【解决方案2】:

    for 循环可用于通过执行向后循环来保持任务列表中项目的顺序。

    我在 TaskList 类中添加了一个方便的方法来轻松访问键

    function getKey($key) {
        return $this->items[$key];
    }
    

    PHP 模板修改为使用 for 循环:

    <form class="" action="index.php" method="post">
      <ul>
        <?php 
          $count = count($taskList->items) - 1;
          for($key=$count; $key >= 0; $key--) {
            $task = $taskList->getKey($key);
        ?>
          <li><input type="checkbox" name="delete[]" value="<?php echo $key ?>">
              <?php echo $task; ?></li>
        <?php  } ?>
      </ul>
      <input type="submit" name="" value="Delete">
    </form>
    

    【讨论】:

      猜你喜欢
      • 2019-05-05
      • 2019-10-31
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      • 2021-07-03
      • 2021-06-09
      • 2018-06-02
      • 2020-11-28
      相关资源
      最近更新 更多