1
|
|
|
<?php
|
2
|
|
|
declare(strict_types=1);
|
3
|
|
|
|
4
|
|
|
namespace SKien\JsonLD;
|
5
|
|
|
|
6
|
|
|
/**
|
7
|
|
|
* Publish your local business structured data.
|
8
|
|
|
* Here you can tell Google about your business hours, different departments
|
9
|
|
|
* within a business, reviews for your business, and more.
|
10
|
|
|
*
|
11
|
|
|
* ### required properties:
|
12
|
|
|
* - @id
|
13
|
|
|
* - [setURL()](#seturl)
|
14
|
|
|
* - PostalAddress
|
15
|
|
|
* - [setAdress()](#setadress)
|
16
|
|
|
* - name
|
17
|
|
|
* - [setInfo()](#setinfo)
|
18
|
|
|
* - telephone
|
19
|
|
|
*
|
20
|
|
|
* ### recommended properties:
|
21
|
|
|
* - url
|
22
|
|
|
* - geolocation
|
23
|
|
|
* - priceRange
|
24
|
|
|
* - openingHours
|
25
|
|
|
* - servesCuisine (recommended for 'FoodEstablishment' and subtypes of it)
|
26
|
|
|
* - logo
|
27
|
|
|
* - image
|
28
|
|
|
*
|
29
|
|
|
* ### additional poroperties
|
30
|
|
|
*
|
31
|
|
|
* #### aggregateRating (NOT supported so far)
|
32
|
|
|
* The average rating of the local company, based on multiple ratings and
|
33
|
|
|
* reviews. Review the guidelines for review snippets and the list of required
|
34
|
|
|
* and recommended attributes for overall ratings.
|
35
|
|
|
*
|
36
|
|
|
* #### department(s)
|
37
|
|
|
* (Type: nested LocalBusiness)
|
38
|
|
|
* Nested element(s) for department(s). In this table you can define any
|
39
|
|
|
* properties for a department.
|
40
|
|
|
* Note: The id MUST differ from the id of the main business!
|
41
|
|
|
* Additional guidelines: Enter the business name with the department name
|
42
|
|
|
* in the following format:
|
43
|
|
|
* {store name} {department name}
|
44
|
|
|
* Example:
|
45
|
|
|
* gMart and gMart Pharmacy.
|
46
|
|
|
*
|
47
|
|
|
* #### menu-card URL (NOT supported so far)
|
48
|
|
|
* The fully qualified URL of the menu (for 'FoodEstablishment' and subtypes of it)
|
49
|
|
|
*
|
50
|
|
|
* #### review (NOT supported so far)
|
51
|
|
|
* A review of the local company. Please refer to the guidelines for review
|
52
|
|
|
* snippets and the list of required and recommended properties for reviews.
|
53
|
|
|
*
|
54
|
|
|
* @link https://developers.google.com/search/docs/data-types/local-business
|
55
|
|
|
* @link https://schema.org/Organization
|
56
|
|
|
* @link https://schema.org/LocalBusiness
|
57
|
|
|
*
|
58
|
|
|
* @package JsonLD
|
59
|
|
|
* @author Stefanius <[email protected]>
|
60
|
|
|
* @copyright MIT License - see the LICENSE file for details
|
61
|
|
|
*/
|
62
|
|
|
class JsonLDLocalBusiness extends JsonLD
|
63
|
|
|
{
|
64
|
|
|
/**
|
65
|
|
|
* Initializes a JsonLD object for local business.
|
66
|
|
|
* Current host is set as URL and internal @id. In most cases this setting
|
67
|
|
|
* should be correct. If different information is required, this setting
|
68
|
|
|
* can be changed using the setURL() method.
|
69
|
|
|
* > Valid values for 'strType' can be found in [Local Business Types](./Local-Business-Types)
|
70
|
|
|
* @param string $strType
|
71
|
|
|
* @param bool $bIsChild
|
72
|
|
|
*/
|
73
|
|
|
public function __construct(string $strType = 'Organization', bool $bIsChild = false)
|
74
|
|
|
{
|
75
|
|
|
parent::__construct(self::LOCAL_BUSINESS, $strType, $bIsChild);
|
76
|
|
|
$strID = $_SERVER['HTTP_HOST'] ?? 'UNKNOWN_HOST';
|
77
|
|
|
if (!$bIsChild) {
|
78
|
|
|
$this->aJsonLD["@id"] = $strID;
|
79
|
|
|
}
|
80
|
|
|
$this->aJsonLD["url"] = $strID;
|
81
|
|
|
}
|
82
|
|
|
|
83
|
|
|
/**
|
84
|
|
|
* Set URL and id of the page.
|
85
|
|
|
* Empty $strId will be set to $strURL. In the constructor the URL and Id is set
|
86
|
|
|
* to the current host. Deviating values must most case only be set for departments
|
87
|
|
|
* or if it is a special subpage of an Internet presence that is treated
|
88
|
|
|
* differently from the main page.
|
89
|
|
|
* @param string $strURL
|
90
|
|
|
* @param string $strId
|
91
|
|
|
*/
|
92
|
|
|
public function setURL(string $strURL, string $strId = '') : void
|
93
|
|
|
{
|
94
|
|
|
$strURL = $this->validURL($strURL);
|
95
|
|
|
$strId = $this->validString($strId);
|
96
|
|
|
if (strlen($strURL) > 0) {
|
97
|
|
|
if (!$this->bIsChild || strlen($strId) > 0) {
|
98
|
|
|
$this->aJsonLD["@id"] = strlen($strId) == 0 ? $strURL : $strId;
|
99
|
|
|
}
|
100
|
|
|
$this->aJsonLD["url"] = $strURL;
|
101
|
|
|
}
|
102
|
|
|
}
|
103
|
|
|
|
104
|
|
|
/**
|
105
|
|
|
* Set base informations about the organisation.
|
106
|
|
|
* @param string $strName mandatory property
|
107
|
|
|
* @param string $strEMail recommended property
|
108
|
|
|
* @param string $strPhone recommended property
|
109
|
|
|
*/
|
110
|
|
|
public function setInfo(string $strName, string $strEMail = '', string $strPhone = '') : void
|
111
|
|
|
{
|
112
|
|
|
$strEMail = $this->validEMail($strEMail);
|
113
|
|
|
$strName = $this->validString($strName);
|
114
|
|
|
if (strlen($strName) > 0) {
|
115
|
|
|
$this->aJsonLD["name"] = $this->validString($strName);
|
116
|
|
|
if (strlen($strEMail) > 0) {
|
117
|
|
|
$this->aJsonLD["email"] = $strEMail;
|
118
|
|
|
}
|
119
|
|
|
$strPhone = $this->validString($strPhone);
|
120
|
|
|
if (strlen($strPhone) > 0) {
|
121
|
|
|
$this->aJsonLD["telephone"] = $strPhone;
|
122
|
|
|
}
|
123
|
|
|
}
|
124
|
|
|
}
|
125
|
|
|
|
126
|
|
|
/**
|
127
|
|
|
* Set postal adress of the business.
|
128
|
|
|
* Here it makes sense to enter as many properties as possible. The more you
|
129
|
|
|
* specify, the more informative the result will be for users.
|
130
|
|
|
* @param string $strStreet
|
131
|
|
|
* @param string $strPostcode
|
132
|
|
|
* @param string $strCity
|
133
|
|
|
* @param string $strRegion (default: '')
|
134
|
|
|
* @param string $strCountry (default: '')
|
135
|
|
|
*/
|
136
|
|
|
public function setAddress(string $strStreet, string $strPostcode, string $strCity, string $strRegion = '', string $strCountry = '') : void
|
137
|
|
|
{
|
138
|
|
|
$this->aJsonLD["address"] = $this->buildAddress($strStreet, $strPostcode, $strCity, $strRegion, $strCountry);
|
139
|
|
|
}
|
140
|
|
|
|
141
|
|
|
/**
|
142
|
|
|
* Set the logo of the organization.
|
143
|
|
|
* @param string $strLogoURL URL to a valid image (PNG, GIF, JPG)
|
144
|
|
|
*/
|
145
|
|
|
public function setLogo(string $strLogoURL) : void
|
146
|
|
|
{
|
147
|
|
|
$aLogo = $this->buildImageObject($strLogoURL);
|
148
|
|
|
if ($aLogo != null) {
|
149
|
|
|
$this->aJsonLD["logo"] = $aLogo;
|
150
|
|
|
}
|
151
|
|
|
}
|
152
|
|
|
|
153
|
|
|
/**
|
154
|
|
|
* The price range of the business, for example $$$.
|
155
|
|
|
* Used by 'LocalBusinesses' and all subtypes of it. If you look closely, plain
|
156
|
|
|
* text is ambiguous in this context... <br/>
|
157
|
|
|
* Haven't really found any good explanation, how to use this property - anyway, google
|
158
|
|
|
* mark it as recomended for 'LocalBusinesses' - just set it to some value ('$', ...).
|
159
|
|
|
* @param string $strPriceRange
|
160
|
|
|
*/
|
161
|
|
|
public function setPriceRange(string $strPriceRange) : void
|
162
|
|
|
{
|
163
|
|
|
$this->setProperty("priceRange", $strPriceRange);
|
164
|
|
|
}
|
165
|
|
|
|
166
|
|
|
/**
|
167
|
|
|
* Recommended for 'FoodEstablishment' and subtypes of it.
|
168
|
|
|
* @param string $strServesCuisine
|
169
|
|
|
*/
|
170
|
|
|
public function setServesCuisine(string $strServesCuisine) : void
|
171
|
|
|
{
|
172
|
|
|
$this->setProperty("servesCuisine", $strServesCuisine);
|
173
|
|
|
}
|
174
|
|
|
|
175
|
|
|
/**
|
176
|
|
|
* Short method to set opening hours.
|
177
|
|
|
* Only set string like i.e. 'Mo.-Fr. 08:00-12:00 13:00-17:30'.
|
178
|
|
|
* Don't use together with extended version addOpeningHours()!
|
179
|
|
|
* @param string $strOpeningHours
|
180
|
|
|
*/
|
181
|
|
|
public function setOpeningHours(string $strOpeningHours) : void
|
182
|
|
|
{
|
183
|
|
|
// "openingHours": "Mo 09:00-12:00 We 12:00-17:00",
|
184
|
|
|
$this->setProperty("openingHours", $strOpeningHours);
|
185
|
|
|
}
|
186
|
|
|
|
187
|
|
|
/**
|
188
|
|
|
* Add valid language.
|
189
|
|
|
* Multiple languages can be set for one business object.
|
190
|
|
|
* @link https://www.w3.org/International/articles/language-tags/
|
191
|
|
|
* @link https://www.w3.org/International/questions/qa-choosing-language-tags
|
192
|
|
|
* @link https://tools.ietf.org/html/bcp47
|
193
|
|
|
* @link https://schneegans.de/lv/
|
194
|
|
|
* @link https://www.npmjs.com/package/bcp47-validate
|
195
|
|
|
* @param string $strLang language in IETF BCP 47 format
|
196
|
|
|
*/
|
197
|
|
|
public function addLanguage(string $strLang) : void
|
198
|
|
|
{
|
199
|
|
|
if (!isset($this->aJsonLD["knowsLanguage"])) {
|
200
|
|
|
$this->aJsonLD["knowsLanguage"] = array();
|
201
|
|
|
}
|
202
|
|
|
$this->aJsonLD["knowsLanguage"][] = $strLang;
|
203
|
|
|
}
|
204
|
|
|
|
205
|
|
|
/**
|
206
|
|
|
* Add a contact to the Object.
|
207
|
|
|
* The type must not contain any predefiend value, use it to describe the contact.
|
208
|
|
|
* (i.e. 'Information', 'Hotline', 'Customer Service', 'Administration', ...)
|
209
|
|
|
* @param string $strType
|
210
|
|
|
* @param string $strEMail
|
211
|
|
|
* @param string $strPhone
|
212
|
|
|
* @return int index of the added contact point
|
213
|
|
|
*/
|
214
|
|
|
public function addContact(string $strType, string $strEMail = '', string $strPhone = '') : int
|
215
|
|
|
{
|
216
|
|
|
$iIndex = -1;
|
217
|
|
|
$aCP = $this->buildContactPoint($strType, $strEMail, $strPhone);
|
218
|
|
|
if ($aCP != null) {
|
219
|
|
|
if (!isset($this->aJsonLD["contactPoint"])) {
|
220
|
|
|
$this->aJsonLD["contactPoint"] = array();
|
221
|
|
|
}
|
222
|
|
|
$iIndex = count($this->aJsonLD["contactPoint"]);
|
223
|
|
|
$this->aJsonLD["contactPoint"][] = $aCP;
|
224
|
|
|
}
|
225
|
|
|
return $iIndex;
|
226
|
|
|
}
|
227
|
|
|
|
228
|
|
|
/**
|
229
|
|
|
* Add language to contact.
|
230
|
|
|
* Multiple languages can be set for one contact.
|
231
|
|
|
* @see JsonLDLocalBusiness::addLanguage()
|
232
|
|
|
* @param int $iContact index of the contact (returned by addContact())
|
233
|
|
|
* @param string $strLang language in IETF BCP 47 format
|
234
|
|
|
*/
|
235
|
|
|
public function addContactLanguage(int $iContact, string $strLang) : void
|
236
|
|
|
{
|
237
|
|
|
if (isset($this->aJsonLD["contactPoint"]) && $iContact < count($this->aJsonLD["contactPoint"])) {
|
238
|
|
|
if (!isset($this->aJsonLD["contactPoint"][$iContact]["availableLanguage"])) {
|
239
|
|
|
$this->aJsonLD["contactPoint"][$iContact]["availableLanguage"] = array();
|
240
|
|
|
}
|
241
|
|
|
$this->aJsonLD["contactPoint"][$iContact]["availableLanguage"][] = $strLang;
|
242
|
|
|
}
|
243
|
|
|
}
|
244
|
|
|
|
245
|
|
|
/**
|
246
|
|
|
* Add URL of a reference Web page that unambiguously indicates the organizations identity.
|
247
|
|
|
* E.g. the URL of the organizations social media page(s)
|
248
|
|
|
* - facebook
|
249
|
|
|
* - twitter
|
250
|
|
|
* - instagramm
|
251
|
|
|
* - wikipedia
|
252
|
|
|
* - ...
|
253
|
|
|
* @param string $strURL
|
254
|
|
|
*/
|
255
|
|
|
public function addSameAs(string $strURL) : void
|
256
|
|
|
{
|
257
|
|
|
$strURL = $this->validURL($strURL);
|
258
|
|
|
if (strlen($strURL) > 0) {
|
259
|
|
|
if (!isset($this->aJsonLD["sameAs"])) {
|
260
|
|
|
$this->aJsonLD["sameAs"] = array();
|
261
|
|
|
}
|
262
|
|
|
$this->aJsonLD["sameAs"][] = $strURL;
|
263
|
|
|
}
|
264
|
|
|
}
|
265
|
|
|
|
266
|
|
|
/**
|
267
|
|
|
* Extended method to set opening hours.
|
268
|
|
|
* <b>!! Don't use together with short version `setOpeningHours()`! </b>
|
269
|
|
|
* Multiple definitions may be set. <br/>
|
270
|
|
|
* e.g.: <br/>
|
271
|
|
|
* <pre><code>Mo 8:00...12:00, 13:00...17:30
|
272
|
|
|
* Tu 8:00...12:00, 13:00...17:30
|
273
|
|
|
* We 8:00...12:00
|
274
|
|
|
* Th 8:00...12:00
|
275
|
|
|
* Fr 8:00...12:00, 13:00...17:30
|
276
|
|
|
*
|
277
|
|
|
* addOpeningHours([1,1,1,1,1,0,0], '8:00', '12:00');
|
278
|
|
|
* addOpeningHours([1,1,0,0,1,0,0], '13:00', '17:30');
|
279
|
|
|
* </code></pre>
|
280
|
|
|
* <br/>
|
281
|
|
|
* @todo openingHoursSpecification.validFrom / openingHoursSpecification.validThrough
|
282
|
|
|
*
|
283
|
|
|
* @param array<int> $aWeekdays array containing 7 elements for each weekday (0-> Monday)
|
284
|
|
|
* @param string $timeOpens time opens
|
285
|
|
|
* @param string $timeCloses time closes
|
286
|
|
|
* @return int index of the added opening hours specification
|
287
|
|
|
*/
|
288
|
|
|
public function addOpeningHours(array $aWeekdays, string $timeOpens, string $timeCloses) : int
|
289
|
|
|
{
|
290
|
|
|
$iIndex = -1;
|
291
|
|
|
$timeOpens = $this->validTime($timeOpens);
|
292
|
|
|
$timeCloses = $this->validTime($timeCloses);
|
293
|
|
|
if (count($aWeekdays) == 7 && strlen($timeOpens) > 0 && strlen($timeCloses) > 0) {
|
294
|
|
|
if (!isset($this->aJsonLD["location"])) {
|
295
|
|
|
$this->aJsonLD["location"] = array("@type" => "Place");
|
296
|
|
|
}
|
297
|
|
|
if (!isset($this->aJsonLD["location"]["openingHoursSpecification"])) {
|
298
|
|
|
$this->aJsonLD["location"]["openingHoursSpecification"] = array();
|
299
|
|
|
}
|
300
|
|
|
$iIndex = count($this->aJsonLD["location"]["openingHoursSpecification"]);
|
301
|
|
|
$aOHS = array("@type" => "OpeningHoursSpecification");
|
302
|
|
|
$aDayOfWeek = array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunnday");
|
303
|
|
|
for ($i = 0; $i < 7; $i++) {
|
304
|
|
|
if ($aWeekdays[$i] != 1) {
|
305
|
|
|
unset($aDayOfWeek[$i]);
|
306
|
|
|
}
|
307
|
|
|
}
|
308
|
|
|
$aDayOfWeek = array_values($aDayOfWeek);
|
309
|
|
|
$aOHS["dayOfWeek"] = $aDayOfWeek;
|
310
|
|
|
$aOHS["opens"] = $timeOpens;
|
311
|
|
|
$aOHS["closes"] = $timeCloses;
|
312
|
|
|
$this->aJsonLD["location"]["openingHoursSpecification"][] = $aOHS;
|
313
|
|
|
}
|
314
|
|
|
return $iIndex;
|
315
|
|
|
}
|
316
|
|
|
|
317
|
|
|
/**
|
318
|
|
|
* Add department to the object.
|
319
|
|
|
* Create a separate LocalBusiness object for each department you want to publish with
|
320
|
|
|
* parameter `$bIsChild` of the constructor set tt `true`.
|
321
|
|
|
* > <b>Important: </b><br/>
|
322
|
|
|
* > !!! The id for each department MUST be set manually and MUST differ
|
323
|
|
|
* from the id of the main (parent) object! <br/>
|
324
|
|
|
* > <b>Additional guideline: </b><br/>
|
325
|
|
|
* > Enter the business name with the department name in the following format: <br/>
|
326
|
|
|
* > <i><b>{store name} {department name} </b> (i.e. 'MyCompany' and 'MyCompany Logistics'</i>.
|
327
|
|
|
*
|
328
|
|
|
* @param JsonLDLocalBusiness $oDepartment
|
329
|
|
|
*/
|
330
|
|
|
public function addDepartment(JsonLDLocalBusiness $oDepartment) : void
|
331
|
|
|
{
|
332
|
|
|
$aDepartment = $oDepartment->getObject();
|
333
|
|
|
if ($aDepartment != null) {
|
334
|
|
|
if (!isset($this->aJsonLD["department"])) {
|
335
|
|
|
$this->aJsonLD["department"] = array();
|
336
|
|
|
}
|
337
|
|
|
$this->aJsonLD["department"][] = $aDepartment;
|
338
|
|
|
}
|
339
|
|
|
}
|
340
|
|
|
}
|
341
|
|
|
|