forked from corestoreio/pkg
/
attribute.go
385 lines (348 loc) · 11.9 KB
/
attribute.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
// Copyright 2015, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package eav
import "errors"
var (
ErrAttributeNotFound = errors.New("Attribute not found")
ErrCollectionEmpty = errors.New("Attribute collection is empty")
)
const (
// TypeStatic use to check if an attribute is static, means part of the eav prefix table
TypeStatic string = "static"
)
type (
// AttributeIndex used for index in a slice with constants (iota)
AttributeIndex uint
// Attributer defines the minimal requirements for one attribute. The interface relates to the table
// eav_attribute. Developers can even extend this table with additional columns. Additional columns
// will result into more generated functions. Even other EAV entities can embed this interface to
// extend an attribute. For example the customer attributes
// extends this interface two times. The column of func AttributeModel() has been removed because
// it is not used and misplaced in the eav_attribute table. This interface can be extended to more than
// 40 functions which is of course not idiomatic but for generated code it provides the best
// flexibility to extend with other custom structs.
// @see magento2/site/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
// @see magento2/site/app/code/Magento/Eav/Api/Data/AttributeInterface.php
Attributer interface {
// IsStatic checks if an attribute is a static one
IsStatic() bool
// EntityType returns EntityType object or an error
EntityType() (*CSEntityType, error)
// UsesSource checks whether possible attribute values are retrieved from a finite source
UsesSource() bool
// IsInSet checks if attribute in specified attribute set
IsInSet(int64) bool
// IsInGroup checks if attribute in specified attribute group
IsInGroup(int64) bool
//@see magento2/site/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
//FlatColumns() []fancyTableColumnType @todo
//FlatIndexes() []fancyTableColumnIndex @todo
//Options()
AttributeID() int64
EntityTypeID() int64
AttributeCode() string
BackendModel() AttributeBackendModeller
BackendType() string
BackendTable() string
FrontendModel() AttributeFrontendModeller
FrontendInput() string
FrontendLabel() string
FrontendClass() string
SourceModel() AttributeSourceModeller
IsRequired() bool
IsUserDefined() bool
DefaultValue() string
IsUnique() bool
Note() string
}
// WSASlice WebSiteAttributeSlice is a slice of pointers to attributes. Despite being a slice the websiteID
// is used as an index to return the attribute for a specific websiteID.
WSASlice []*Attribute
// Attribute defines properties for an attribute. This struct must be embedded in other EAV attributes.
Attribute struct {
// wa contains website specific attribute properties which are pulled out from the table
// e.g. customer_eav_attribute_website or any other entity eav website table.
// This pointer slice is in most cases nil. All struct fields can have different values in a website.
wa WSASlice
// websiteID if 0 then default values. If greater 0 then we have different values for a website.
websiteID int64
attributeID int64
entityTypeID int64
attributeCode string
backendModel AttributeBackendModeller
backendType string
backendTable string
frontendModel AttributeFrontendModeller
frontendInput string
frontendLabel string
frontendClass string
sourceModel AttributeSourceModeller
isRequired bool
isUserDefined bool
defaultValue string
isUnique bool
note string
}
// AttributeGetter implements functions on how to retrieve directly a certain attribute. This interface
// is used in concrete attribute models by generated code.
// The logic behind this interface is to provide a fast access to the AttributeIndex. We will use as
// key the id int64 or code string which will then map to the value of an AttributeIndex.
AttributeGetter interface {
// ByID returns an index using the AttributeID. This index identifies an attribute within an AttributeSlice.
ByID(id int64) (AttributeIndex, error)
// ByCode returns an index using the AttributeCode. This index identifies an attribute within an AttributeSlice.
ByCode(code string) (AttributeIndex, error)
}
AttributeSliceGetter interface {
Index(i AttributeIndex) interface{}
Len() int
ByID(g AttributeGetter, id int64) (interface{}, error)
ByCode(g AttributeGetter, code string) (interface{}, error)
}
)
var _ Attributer = (*Attribute)(nil)
var _ AttributeGetter = (*AttributeMapGet)(nil)
// Index returns the attribute from the current index or nil
func (s WSASlice) Index(i int64) *Attribute {
if i > int64(len(s)) || i < 0 {
return nil
}
return s[i] // can also be nil
}
// NewAttributeMapGet returns a new pointer to an AttributeMapGet.
func NewAttributeMapGet(i map[int64]AttributeIndex, c map[string]AttributeIndex) *AttributeMapGet {
return &AttributeMapGet{
i: i,
c: c,
}
}
// AttributeMapGet contains two maps for faster retrieving of the attribute index.
// Only used in generated code. Implements interface AttributeGetter.
type AttributeMapGet struct {
i map[int64]AttributeIndex
c map[string]AttributeIndex
}
// ByID returns an attribute index by the id from the database table
func (si *AttributeMapGet) ByID(id int64) (AttributeIndex, error) {
if i, ok := si.i[id]; ok {
return i, nil
}
return AttributeIndex(0), ErrAttributeNotFound
}
// ByCode returns an attribute index
func (si *AttributeMapGet) ByCode(code string) (AttributeIndex, error) {
if c, ok := si.c[code]; ok {
return c, nil
}
return AttributeIndex(0), ErrAttributeNotFound
}
// NewAttribute only for use in auto generated code. Looks terrible 8-)
func NewAttribute(
wa WSASlice,
websiteID int64,
attributeCode string,
attributeID int64,
backendModel AttributeBackendModeller,
backendTable string,
backendType string,
defaultValue string,
entityTypeID int64,
frontendClass string,
frontendInput string,
frontendLabel string,
frontendModel AttributeFrontendModeller,
isRequired bool,
isUnique bool,
isUserDefined bool,
note string,
sourceModel AttributeSourceModeller,
) *Attribute {
return &Attribute{
wa: wa,
websiteID: websiteID,
attributeID: attributeID,
entityTypeID: entityTypeID,
attributeCode: attributeCode,
backendModel: backendModel,
backendType: backendType,
backendTable: backendTable,
frontendModel: frontendModel,
frontendInput: frontendInput,
frontendLabel: frontendLabel,
frontendClass: frontendClass,
sourceModel: sourceModel,
isRequired: isRequired,
isUserDefined: isUserDefined,
defaultValue: defaultValue,
isUnique: isUnique,
note: note,
}
}
// hasWA checks if the attribute has a website specific attribute
func (a *Attribute) hasWA() bool {
return a.websiteID > 0 && a.wa.Index(a.websiteID) != nil
}
// IsStatic checks if an attribute is a static one. Considers websiteID.
func (a *Attribute) IsStatic() bool {
wa := a
if a.hasWA() {
wa = a.wa.Index(a.websiteID)
}
return wa.backendType == TypeStatic || wa.backendType == ""
}
// EntityType returns EntityType object or an error. Does not consider websiteID.
func (a *Attribute) EntityType() (*CSEntityType, error) {
return GetEntityTypeCollection().GetByID(a.entityTypeID)
}
/*
@todo implement wa check like in IsStatic()
*/
// UsesSource checks whether possible attribute values are retrieved from a finite source
func (a *Attribute) UsesSource() bool {
switch {
case a.frontendInput == "select":
return true
case a.frontendInput == "multiselect":
return true
case a.sourceModel != nil:
return true
}
return false
}
// IsInSet checks if attribute in specified attribute set @todo
func (a *Attribute) IsInSet(_ int64) bool {
return false
}
// IsInGroup checks if attribute in specified attribute group @todo
func (a *Attribute) IsInGroup(_ int64) bool {
return false
}
func (a *Attribute) AttributeID() int64 {
return a.attributeID
}
func (a *Attribute) EntityTypeID() int64 {
return a.entityTypeID
}
func (a *Attribute) AttributeCode() string {
return a.attributeCode
}
func (a *Attribute) BackendModel() AttributeBackendModeller {
return a.backendModel
}
func (a *Attribute) BackendType() string {
return a.backendType
}
// BackendTable returns the attribute backend table name. This function panics.
// @see magento2/site/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php::getBackendTable
func (a *Attribute) BackendTable() string {
et, err := a.EntityType()
if err != nil {
panic("EntityType not found for attribute " + a.attributeCode)
}
if a.IsStatic() {
// this means that the attribute is directly a column of the base entity table like
// catalog_product_entity or customer_entity or eav_entity
return et.GetValueTablePrefix()
}
if a.backendTable != "" {
return a.backendTable
}
return et.GetEntityTablePrefix() + "_" + a.backendType
}
func (a *Attribute) FrontendModel() AttributeFrontendModeller {
return a.frontendModel
}
func (a *Attribute) FrontendInput() string {
return a.frontendInput
}
func (a *Attribute) FrontendLabel() string {
return a.frontendLabel
}
func (a *Attribute) FrontendClass() string {
return a.frontendClass
}
func (a *Attribute) SourceModel() AttributeSourceModeller {
return a.sourceModel
}
func (a *Attribute) IsRequired() bool {
return a.isRequired
}
func (a *Attribute) IsUserDefined() bool {
return a.isUserDefined
}
func (a *Attribute) DefaultValue() string {
return a.defaultValue
}
func (a *Attribute) IsUnique() bool {
return a.isUnique
}
func (a *Attribute) Note() string {
return a.note
}
// Handler internal wrapper for attribute collection C, getter G and entity type id.
// must be embedded into a concrete attribute struct. Implements interface EntityTypeAttributeModeller
// and EntityTypeAttributeCollectioner.
type Handler struct {
// EntityTyeID to load the entity type. @todo implementation
EntityTyeID int64
// C collection of a materialized slice
C AttributeSliceGetter
// G getter knows how to return an AttributeIndex by id or by code
G AttributeGetter
}
var _ EntityTypeAttributeModeller = (*Handler)(nil)
var _ EntityTypeAttributeCollectioner = (*Handler)(nil)
// New creates a new attribute and returns interface custattr.Attributer
func (h *Handler) New() interface{} {
panic("Please override this method")
return nil
}
// Get uses an AttributeIndex to return an attribute or an error.
// Use type assertion to convert to Attributer.
func (h *Handler) Get(i AttributeIndex) (interface{}, error) {
if h.C != nil && int(i) < h.C.Len() {
return h.C.Index(i), nil
}
return nil, ErrAttributeNotFound
}
// MustGet returns an attribute by AttributeIndex. Panics if not found.
// Use type assertion to convert to Attributer.
func (h *Handler) MustGet(i AttributeIndex) interface{} {
a, err := h.Get(i)
if err != nil {
panic(err)
}
return a
}
// GetByID returns an address attribute by its id
// Use type assertion to convert to Attributer.
func (h *Handler) GetByID(id int64) (interface{}, error) {
if h.C != nil {
return h.C.ByID(h.G, id)
}
return nil, ErrCollectionEmpty
}
// GetByCode returns an address attribute by its code
// Use type assertion to convert to Attributer.
func (h *Handler) GetByCode(code string) (interface{}, error) {
if h.C != nil {
return h.C.ByCode(h.G, code)
}
return nil, ErrCollectionEmpty
}
// Collection returns the full attribute collection AttributeSlice.
// You must use type assertion to convert to custattr.AttributeSlice.
func (h *Handler) Collection() interface{} {
return h.C
}