Add Silverstripe DateField

User
add Silverstripe 5 DateField
Assistant
In **Silverstripe 5**, you can add a `DateField` to your form by using the built-in form field class `SilverStripeFormsDateField`.

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**?

User
public function doStep13(HTTPRequest $request){
$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
Assistant
Got it 👍 — in **Silverstripe 5**, you can use the session to store a flash message and then display it on the `brokerapartments` page.

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`**?

User
$ApartmentFlashMessage = $request->getSession()->get('ApartmentFlashMessage');
$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

Assistant
Perfect — you’re already passing `Apartments` and the flash message. Here’s a clean way to **render the listing** (with an empty state + optional pagination).

### 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.

User
google map place url by address
Assistant
If you already have the **address string**, you can directly build a Google Maps "place" (search) URL from it.

### 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?

User
public function getAbdate()
{

return date('d-m-Y', strtotime($this->FloorVerfugbarAb));
}
check if FloorVerfugbarAb is correct format in db 2025-09-08 then apply date

Assistant
Got it ✅ — since your DB column `FloorVerfugbarAb` is stored as `YYYY-MM-DD` (e.g. `2025-09-08`), you should first check if it’s a valid date string before formatting.

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)?

User
private function getApartmentFromRequest(HTTPRequest $request): ?Apartment
{
$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

Assistant
You're getting that error because your helper is declared to return `?Apartment` but in the `else` branch you return a redirect (`HTTPResponse`). Pick one behavior:

## 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.

User
its still not redirect just go to funciton from which its called
Assistant
Right — in Silverstripe a redirect only “takes effect” if the **controller action returns the HTTPResponse**.
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.

User
Assistant
Thanks for sharing the full controller — I see why you’re running into this.
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?

User
protected function init()
{
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()

Assistant
Good question 👍

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)?

Leave a Comment

Your email address will not be published. Required fields are marked *