Add Silverstripe DateField
August 21, 2025 · AI Conversations
Here’s a simple example:
use SilverStripeFormsForm;
use SilverStripeFormsFieldList;
use SilverStripeFormsDateField;
use SilverStripeFormsFormAction;
use SilverStripeFormsRequiredFields;
public function MyForm()
{
$fields = FieldList::create(
DateField::create('StartDate', 'Start Date')
->setHTML5(true) // enables native browser date picker
->setDescription('Select a date'),
DateField::create('EndDate', 'End Date')
->setHTML5(true)
);
$actions = FieldList::create(
FormAction::create('doSubmit', 'Submit')
);
$validator = RequiredFields::create('StartDate', 'EndDate');
return Form::create($this, 'MyForm', $fields, $actions, $validator);
}
### Notes:
– `setHTML5(true)` → uses the browser’s native date picker.
– If you want Silverstripe’s **jQuery UI date picker** (instead of HTML5), remove `setHTML5(true)`. Silverstripe will then fall back to its JS date picker.
– In the template (`.ss` file), just output your form:
$MyForm
👉 Do you want the **HTML5 date input** (native browser calendar) or the **jQuery date picker**?
$data = $request->postVars();
$apartment = $this->getApartmentFromRequest($request);
if($apartment){
$apartment->ContactId = $data['ContactId'];
$apartment->write();
}
return $this->redirect('/dashboard/brokerapartments');
}
also pass a flash message and show brokerapartments
Here’s how you can update your controller method:
use SilverStripeControlHTTPRequest;
use SilverStripeSecuritySecurity;
public function doStep13(HTTPRequest $request)
{
$data = $request->postVars();
$apartment = $this->getApartmentFromRequest($request);
if ($apartment) {
$apartment->ContactId = $data['ContactId'];
$apartment->write();
// Set success flash message
$request->getSession()->set('FlashMessage', 'Apartment contact updated successfully.');
} else {
// Set error flash message
$request->getSession()->set('FlashMessage', 'Apartment not found.');
}
return $this->redirect('/dashboard/brokerapartments');
}
### In your `brokerapartments` action:
You need to pass the flash message to your template.
public function brokerapartments(HTTPRequest $request)
{
$message = $request->getSession()->get('FlashMessage');
$request->getSession()->clear('FlashMessage'); // clear after showing
return $this->customise([
'FlashMessage' => $message,
])->renderWith(['Dashboard/BrokerApartments', 'Page']);
}
### In your template (`BrokerApartments.ss`):
<% if $FlashMessage %>
<div class="alert alert-success">
$FlashMessage
</div>
<% end_if %>
—
👉 Do you want the **same template (`Page.ss`)** to handle flash messages globally (like WordPress notices), or only **inside `brokerapartments`**?
$request->getSession()->clear('ApartmentFlashMessage');
$member = Security::getCurrentUser();
$records = MemberCompanyData::get()
->filter('MemberID', $member->ID)
->first();
if ($records) {
$companyData = $records->toMap();
}
$firstName = $request->getSession()->get('CUFirstName');
$lastName = $request->getSession()->get('CULastName');
return $this->customise([
'Title' => 'Alle Ihre wohnungen',
'Name' => $firstName.' '.$lastName,
'ApartmentFlashMessage' => $ApartmentFlashMessage,
'Apartments' => Apartment::get()->filter('MemberID', $member->ID)->sort('ID', 'ASC')
])->renderWith(['Layout/Broker/BrokerApartments', 'Page']);
Now show listing Apartments
### 1) (Optional) Controller: paginate the list
use SilverStripeORMPaginatedList;
$apartments = Apartment::get()
->filter('MemberID', $member->ID)
->sort('ID', 'DESC');
$paginated = PaginatedList::create($apartments, $request);
$paginated->setPageLength(10);
return $this->customise([
'Title' => 'Alle Ihre wohnungen',
'Name' => $firstName.' '.$lastName,
'ApartmentFlashMessage' => $ApartmentFlashMessage,
'Apartments' => $paginated // or $apartments if you don't want pagination
])->renderWith(['Layout/Broker/BrokerApartments', 'Page']);
### 2) Template: `themes/…/templates/Layout/Broker/BrokerApartments.ss`
<% if $ApartmentFlashMessage %>
<div class="alert alert-success">$ApartmentFlashMessage</div>
<% end_if %>
<h1>$Title</h1>
<% if $Apartments.Exists %>
<table class="table">
<thead>
<tr>
<th>#ID</th>
<th>Objekt-Nr.</th>
<th>Gruppen-Nr.</th>
<th>Wohnungstyp</th>
<th>Erstellt</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<% loop $Apartments %>
<tr>
<td>$ID</td>
<td>$ObjectNumber</td>
<td>$GroupNumber</td>
<td>$WohnungType</td>
<td>$Created.Nice</td>
<td>
<a href="/dashboard/apartment/$ID" class="btn btn-sm btn-primary">Ansehen</a>
<a href="/dashboard/apartment/$ID/edit" class="btn btn-sm btn-secondary">Bearbeiten</a>
</td>
</tr>
<% end_loop %>
</tbody>
</table>
<% if $Apartments.MoreThanOnePage %>
<nav class="pagination">
<% if $Apartments.NotFirstPage %>
<a href="$Apartments.PrevLink">Zurück</a>
<% end_if %>
<% loop $Apartments.Pages %>
<% if $CurrentBool %>
<span class="current">$PageNum</span>
<% else %>
<a href="$Link">$PageNum</a>
<% end_if %>
<% end_loop %>
<% if $Apartments.NotLastPage %>
<a href="$Apartments.NextLink">Weiter</a>
<% end_if %>
</nav>
<% end_if %>
<% else %>
<div class="alert alert-info">
Noch keine Wohnungen vorhanden.
<a href="/dashboard/apartment/new" class="btn btn-primary btn-sm">Jetzt hinzufügen</a>
</div>
<% end_if %>
That’s it.
– If you **don’t need pagination**, skip the PaginatedList part and keep using your existing `$Apartments = Apartment::get()->…`.
– Update the **view/edit URLs** to match your routes.
### Simple way (no API needed):
$address = "1600 Amphitheatre Parkway, Mountain View, CA";
$url = "https://www.google.com/maps/place/" . urlencode($address);
echo $url;
👉 Result will look like:
https://www.google.com/maps/place/1600+Amphitheatre+Parkway,+Mountain+View,+CA
Clicking this opens the place in Google Maps.
—
### If you want the official **Google Maps Place ID URL** (using API):
1. Call the [Places API](https://developers.google.com/maps/documentation/places/web-service/search) to get the **place_id** for an address.
2. Build the URL:
https://www.google.com/maps/place/?q=place_id:PLACE_ID
Example:
$placeId = "ChIJ2eUgeAK6j4ARbn5u_wAGqWA"; // retrieved from Places API
$url = "https://www.google.com/maps/place/?q=place_id:" . $placeId;
—
⚡ For most cases, if you just need a clickable Google Maps link for an **apartment address**, the first (simple `urlencode($address)`) method is enough.
Do you want me to show you how to **integrate this into your Apartment listing template** so each apartment shows a “View on Google Maps” link?
{
return date('d-m-Y', strtotime($this->FloorVerfugbarAb));
}
check if FloorVerfugbarAb is correct format in db 2025-09-08 then apply date
Here’s a safe way in your **DataObject**:
public function getAbdate()
{
$value = $this->FloorVerfugbarAb;
if ($value && preg_match('/^d{4}-d{2}-d{2}$/', $value)) {
// Valid YYYY-MM-DD format → format to d-m-Y
return date('d-m-Y', strtotime($value));
}
// Fallback: return as-is or empty
return $value ?: '';
}
### Explanation:
– `preg_match('/^d{4}-d{2}-d{2}$/', $value)` → ensures the value looks like `YYYY-MM-DD`.
– `strtotime($value)` safely converts it to a timestamp.
– `date('d-m-Y', …)` outputs `08-09-2025`.
– If the DB value is empty or not in the right format, you’ll just return it raw (or `''`).
—
👉 Do you want me to also handle **Silverstripe `DBDate` fields** directly (so you can use `$FloorVerfugbarAb.Nice` in templates instead of making a custom getter)?
{
$member = Security::getCurrentUser();
if (!$member) {
return null;
}
$id = (int) $request->getVar('apartmentID');
if (!$id) {
return null;
}
/** @var Apartment|null $apt */
$apt = Apartment::get()->byID($id);
if ($apt && (int)$apt->MemberID === (int)$member->ID) {
return $apt;
}else{
return $this->redirect('/apartments/notfound');
}
}
this not work because we Return Apartment
## Option A (recommended): keep it a pure finder and handle redirects in the action
use SilverStripeSecuritySecurity;
use SilverStripeControlHTTPRequest;
use AppModelApartment;
private function getApartmentFromRequest(HTTPRequest $request): ?Apartment
{
$member = Security::getCurrentUser();
if (!$member) {
return null;
}
$id = (int) $request->getVar('apartmentID');
if (!$id) {
return null;
}
return Apartment::get()
->filter(['ID' => $id, 'MemberID' => (int)$member->ID])
->first();
}
Then in your action (e.g., `doStep13`):
public function doStep13(HTTPRequest $request)
{
$apt = $this->getApartmentFromRequest($request);
if (!$apt) {
$request->getSession()->set('ApartmentFlashMessage', 'Apartment not found or access denied.');
return $this->redirect('/apartments/notfound');
}
$data = $request->postVars();
$apt->ContactId = $data['ContactId'] ?? null;
$apt->write();
$request->getSession()->set('ApartmentFlashMessage', 'Apartment contact updated successfully.');
return $this->redirect('/dashboard/brokerapartments');
}
## Option B: make the helper able to return a redirect (less clean)
Change the return type and handle both possibilities in callers:
use SilverStripeControlHTTPResponse;
private function getApartmentFromRequest(HTTPRequest $request): Apartment|HTTPResponse|null
{
// ...
if (!$apt) {
return $this->redirect('/apartments/notfound');
}
return $apt;
}
Then in the action:
$res = $this->getApartmentFromRequest($request);
if ($res instanceof HTTPResponse) return $res;
if (!$res) { /* handle null */ }
Most projects prefer **Option A** for clear separation: helpers return data (or null), actions decide navigation.
If you call `$this->redirect()` inside a helper and don’t return that response from the **public action**, the browser won’t navigate.
Use an early‑return pattern:
use SilverStripeControlHTTPRequest;
use SilverStripeControlHTTPResponse;
use SilverStripeSecuritySecurity;
use AppModelApartment;
private function getApartmentFromRequest(HTTPRequest $request): ?Apartment
{
$member = Security::getCurrentUser();
if (!$member) {
return null;
}
$id = (int) $request->getVar('apartmentID');
if (!$id) {
return null;
}
return Apartment::get()
->filter(['ID' => $id, 'MemberID' => (int)$member->ID])
->first();
}
public function doStep13(HTTPRequest $request)
{
$apt = $this->getApartmentFromRequest($request);
if (!$apt) {
$request->getSession()->set('ApartmentFlashMessage', 'Apartment not found or access denied.');
// IMPORTANT: return this from the ACTION
return $this->redirect('/apartments/notfound');
}
$data = $request->postVars();
$apt->ContactId = $data['ContactId'] ?? null;
$apt->write();
$request->getSession()->set('ApartmentFlashMessage', 'Apartment contact updated successfully.');
return $this->redirect('/dashboard/brokerapartments');
}
If you really want the helper to decide redirection, make it **return the response** and propagate it:
private function requireApartmentOrRedirect(HTTPRequest $request): Apartment|HTTPResponse
{
$apt = $this->getApartmentFromRequest($request);
if ($apt) return $apt;
return $this->redirect('/apartments/notfound'); // returns HTTPResponse
}
public function doStep13(HTTPRequest $request)
{
$res = $this->requireApartmentOrRedirect($request);
if ($res instanceof HTTPResponse) {
return $res; // <- must return from ACTION
}
$res->ContactId = $request->postVar('ContactId');
$res->write();
$request->getSession()->set('ApartmentFlashMessage', 'Apartment contact updated successfully.');
return $this->redirect('/dashboard/brokerapartments');
}
Quick checks if it still “doesn’t redirect”:
– Ensure `doStep13` is in `$allowed_actions`.
– Make sure you’re not rendering after the redirect (no further `customise()->renderWith()`).
– If this is an **AJAX/fetch** POST, the browser won’t navigate on 302 by itself—return JSON like `{"redirect": "/apartments/notfound"}` and handle it on the client.
Right now your helper `getApartmentFromRequest()` just returns `null`. Every action then continues rendering, so you never actually redirect to `notfound`.
### 🔑 Best way: let the helper **return a redirect response**, and all actions must handle it early.
Here’s how you can do it:
use SilverStripeControlHTTPResponse;
// replace your current helper
private function getApartmentFromRequest(HTTPRequest $request): Apartment|HTTPResponse|null
{
$member = Security::getCurrentUser();
if (!$member) {
return $this->redirect('/login'); // force login if not logged in
}
$id = (int) $request->getVar('apartmentID');
if (!$id) {
return $this->redirect('/apartment/notfound');
}
$apt = Apartment::get()->byID($id);
if ($apt && (int)$apt->MemberID === (int)$member->ID) {
return $apt;
}
// redirect to notfound if mismatch
return $this->redirect('/apartment/notfound');
}
### Example usage in an action:
public function step3(HTTPRequest $request)
{
$apt = $this->getApartmentFromRequest($request);
if ($apt instanceof HTTPResponse) {
return $apt; // immediately redirect
}
$form = $this->step3Form($apt);
// …
}
### Why this works:
– Your helper enforces membership and ownership.
– If validation fails, it returns an `HTTPResponse` from `$this->redirect()`.
– Each public action only needs the **one-line check**:
if ($apt instanceof HTTPResponse) return $apt;
That way you get a **global redirect to `/apartment/notfound`** whenever the apartment doesn’t belong to the current member.
—
👉 Do you want me to quickly refactor **all your steps (step1–step13)** with that `if ($apt instanceof HTTPResponse)` check so you can copy-paste directly?
{
parent::init();
// Check login status
if (!Security::getCurrentUser()) {
return $this->redirect('/login');
}
$id = (int) $request->getVar('apartmentID');
if($id){
$apt = Apartment::get()->byID($id);
if (!$apt || (int)$apt->MemberID !== (int)Security::getCurrentUser()->ID) {
return $this->redirect('/apartment/notfound');
}
}
}
How to access $request in init()
In **`init()`** you don’t get `$request` injected automatically.
But you can always fetch the current request from the controller itself:
protected function init()
{
parent::init();
// you can access it like this
$request = $this->getRequest();
// Check login
if (!Security::getCurrentUser()) {
return $this->redirect('/login');
}
$id = (int) $request->getVar('apartmentID');
if ($id) {
$apt = Apartment::get()->byID($id);
if (!$apt || (int)$apt->MemberID !== (int)Security::getCurrentUser()->ID) {
return $this->redirect('/apartment/notfound');
}
}
}
👉 `getRequest()` is always available on `Controller` and descendants like `ContentController`.
That way you can enforce login **and** ownership check globally for every action in `ApartmentController`.
Do you want me to also show you how to skip this check for certain actions (like `index` or `notfound` itself)?