Tricky 'previous page' function

Silverstripe Version: 3.7.3

Tricky ‘previous page’ function

The problem

I’m updating an older website that showcases Student work. Each ‘Project Page’ now requires little Next and Previous arrows that link to the Next project under that ‘Student’ page. When there are no more projects under that Student it moves on to the next Student etc — so that you can click through all of the work using these arrows.
[Each Student Page is set to auto redirect to it’s first Project]

I grabbed some Next/Previous page code from Silverstripe Forum and the Next button is working perfectly, doing exactly what I want it to do.

The Previous button is working when within a Student Page (will go back to the previous project) but the problem is when it has to jump out of that student page and go to the previous Student.

Currently the bit of code at the end of this function tells it to jump back up to the parent if there’s no more children.

// nothing returned so we jump to the parent page

if ($parent > 0) return $this->getParent();

But because of the structure of this website it automatically redirects back to the first project under that same Student.

The required solution

What I actually need it to do is go to the last child of previous Parent .

Here’s the full NextPage and PreviousPage functions I’m using (NextPage is working perfectly for me):

function NextPage($id=null) {
		if ($id===null) {
			// if page has a child go to the first child page
			$children = $this->AllChildren();
			foreach ($children as $child) {
				return $child;	
			}
			$parent = $this->ParentID;
			$sort = $this->Sort;
		} else {
			$page = DataObject::get_by_id("SiteTree", $id);
			$parent = $page->ParentID;
			$sort = $page->Sort;
		}
		$where = "ParentID = $parent AND `SiteTree`.ID != 4 AND Sort > $sort";
		$pages = DataObject::get("SiteTree", $where, "Sort", "", 1);
		if($pages) {
			foreach($pages as $page) {
				return $page;
			}
		}
		// nothing returned so we recurse up through parents
		if ($parent > 0) return $this->NextPage($parent);
	}// end of NextPage funtion
	
	
	
	function PreviousPage() {
  	$parent = $this->ParentID;
		$sort = $this->Sort;
		$where = "ParentID = $parent AND Sort < $sort";
		$pages = DataObject::get("SiteTree", $where, "Sort DESC", "", 1);
		if($pages) {
			foreach($pages as $page) {
				// if page has a child go to the last child page
				$children = $page->AllChildren();
				 if ($children->Count()) {
					foreach ($children as $child) {
						continue;
					}
					return $child;
				}				
				return $page;
			}
		}
		// nothing returned so we jump to the parent page
		if ($parent > 0) return $this->getParent();
	}

is it a ‘one level nested only’ structure like
student 1

  • project 1A
  • project 1B
  • project 1C

student 2

  • project 2A
  • project 2B

etc. ?

And do you use page types, i.e. StudentPage and ProjectPage classes?

Yes and Yes. That’s exactly right

Solved. For anyone interested here’s the final code used:

function PreviousPage() {
	  $parent = $this->ParentID;
		$sort = $this->Sort;
		$where = "ParentID = $parent AND Sort < $sort";
		$pages = DataObject::get("SiteTree", $where, "Sort DESC", "", 1);
		if($pages) {
			foreach($pages as $page) {
				// if page has a child go to the last child page
				$children = $page->AllChildren();
				 if ($children->Count()) {
					foreach ($children as $child) {
						continue;
					}
					return $child;
				}				
				return $page;
			}
		}
		// nothing returned so we jump to the parent page
		if ($parent > 0) {
			//Get all StudentPages in this section
			$studentPages = StudentPage::get()
				->filter(['ParentID' => $this->Parent->Parent->ID])
				->sort();

			$studentPages = $studentPages->toArray();

			//Current page
			$studentPage = StudentPage::get()->byID($parent);
			$i = 0;
			foreach ($studentPages as $page) {
				if ($page->ID == $studentPage->ID) {
					break;
				}
				$i++;
			}

			//Sort -2 because Sort goes from 1, but array goes from 0
			while (isset($studentPages[$i-1])) {
				$children = $studentPages[$i-1]->AllChildren()->toArray();
				if (count($children)) {
					return array_reverse($children)[0];
				}
				else {
					$i--;
				}

			}
			return $this->Parent->Parent->getParent();
			
		}
	}