Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/sroehrl/broadcast

3-way databinding: PHP, database, clients
https://github.com/sroehrl/broadcast

Last synced: 10 days ago
JSON representation

3-way databinding: PHP, database, clients

Awesome Lists containing this project

README

        

# 3-way data-binding with PHP & socket.io

Keep multiple clients & your database in sync!

Designed for [LENKRAD](https://lenkrad.neoan3.rocks), but agnostic enough to be used in any framework.

> Make any database a live-database

## Installation

### 1. install package

`composer require neoan.io/broadcast`

### 2. Add required ENV-variables to your .env
- JWT_SECRET (your encryption key)
- SOCKET_SERVER_PORT (e.g. 3000)
- SOCKET_SERVER_URL (e.g. "localhost")

### 3. Add the following script to your composer.json
```json
...
"scripts": {
"install-socket": "NeoanIo\\MarketPlace\\Broadcast\\NpmHandler::package"
}
...
```
### 4. Run said script

`composer run install-socket`

### 5. Install required node packages

`yarn install` or `npm install`

### 6. Test socket-server

`yarn sockert-server` or `npm run socket-server`

## Setup

### Client functionality

This package includes a client-library to automate synchronization with your database.
In LENKRAD, you would typically expose the library via a route:

```php

import {SyncEntity, HTMLBinder} from "/client.js";

```

### Backend

Two things need to happen in order for the synchronization to work.
Firstly, a returned entity must expose the socket-information (including auth).
We can achieve this with `ForClient::wrapEntity`. A typical API-route
could look like this:

```php
toArray())
->toRoom('notes')
->withId($model->id)
->broadcast();

}
}
```

Next, we want to set a hook for whenever the model is written to.
In LENKRAD, we can use the `Model::afterStore` method to achieve this.
Our Note-model could look like this:

```php
withId($this->id)
->withBody($this->toArray())
->emit();
}

}
```
We don't have to worry about any specific code in our update routes.
Given the example, it could look like this:

```php
content = Request::getInput('content');
return $find->store();
}
}
```

## Frontend usage

```javascript
// taken from earlier example
import {SyncEntity, HTMLBinder} from "/client.js";

// allow for programmatic updates on any change (without event)
// careful: this produces a lot of traffic and is usually not necessary)
let updateOnAnyChange = true;

// SyncEntity grabs the wrapped entity via GET
// (the PUT-request needs ot be in the same format)
// SyncEntity returns a proxy triggering PUT-requests as needed,
// and receiving updates via socket alike!
let note = await SyncEntity('/api/note/1', updateOnAnyChange)

// if `updateOnAnyChange` is true, every change will be broadcasted
setTimeout(()=> {
note.content += '!';
updateOnAnyChange = false;
}, 1000)

// however, we usually want to trigger on specific events
const binder = new HTMLBinder(note)
// e.g. on form submission

binder.toForm('#my-form')

// or directly on the element-level

binder.toElements({
'#my-input':{
property: 'content',
event: 'blur'
}
})

```

## Acknowledgements

The proxy-logic is based on a modified version of Sindre Sorhus' [on-change](https://github.com/sindresorhus/on-change).
Make sure to leave a star ;-)