github.com/goravel/framework@v1.13.9/database/factory.go (about)

     1  package database
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	"github.com/mitchellh/mapstructure"
     9  
    10  	"github.com/goravel/framework/contracts/database/factory"
    11  	ormcontract "github.com/goravel/framework/contracts/database/orm"
    12  )
    13  
    14  type FactoryImpl struct {
    15  	count *int              // number of models to generate
    16  	query ormcontract.Query // query instance
    17  }
    18  
    19  func NewFactoryImpl(query ormcontract.Query) *FactoryImpl {
    20  	return &FactoryImpl{
    21  		query: query,
    22  	}
    23  }
    24  
    25  // Count Specify the number of models you wish to create / make.
    26  func (f *FactoryImpl) Count(count int) ormcontract.Factory {
    27  	return f.newInstance(map[string]any{"count": count})
    28  }
    29  
    30  // Create a model and persist it in the database.
    31  func (f *FactoryImpl) Create(value any, attributes ...map[string]any) error {
    32  	if err := f.Make(value, attributes...); err != nil {
    33  		return err
    34  	}
    35  
    36  	return f.query.Create(value)
    37  }
    38  
    39  // CreateQuietly create a model and persist it in the database without firing any events.
    40  func (f *FactoryImpl) CreateQuietly(value any, attributes ...map[string]any) error {
    41  	if err := f.Make(value, attributes...); err != nil {
    42  		return err
    43  	}
    44  
    45  	return f.query.WithoutEvents().Create(value)
    46  }
    47  
    48  // Make a model instance that's not persisted in the database.
    49  func (f *FactoryImpl) Make(value any, attributes ...map[string]any) error {
    50  	reflectValue := reflect.Indirect(reflect.ValueOf(value))
    51  	switch reflectValue.Kind() {
    52  	case reflect.Array, reflect.Slice:
    53  		count := 1
    54  		if f.count != nil {
    55  			count = *f.count
    56  		}
    57  		for i := 0; i < count; i++ {
    58  			elemValue := reflect.New(reflectValue.Type().Elem()).Interface()
    59  			attributes, err := f.getRawAttributes(elemValue, attributes...)
    60  			if err != nil {
    61  				return err
    62  			}
    63  			if attributes == nil {
    64  				return errors.New("failed to get raw attributes")
    65  			}
    66  			decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
    67  				Squash: true,
    68  				Result: elemValue,
    69  			})
    70  			if err != nil {
    71  				return err
    72  			}
    73  			if err := decoder.Decode(attributes); err != nil {
    74  				return err
    75  			}
    76  			reflectValue = reflect.Append(reflectValue, reflect.ValueOf(elemValue).Elem())
    77  		}
    78  
    79  		reflect.ValueOf(value).Elem().Set(reflectValue)
    80  
    81  		return nil
    82  	default:
    83  		attributes, err := f.getRawAttributes(value, attributes...)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		if attributes == nil {
    88  			return errors.New("failed to get raw attributes")
    89  		}
    90  		decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
    91  			Squash: true,
    92  			Result: value,
    93  		})
    94  		if err != nil {
    95  			return err
    96  		}
    97  
    98  		return decoder.Decode(attributes)
    99  	}
   100  }
   101  
   102  func (f *FactoryImpl) getRawAttributes(value any, attributes ...map[string]any) (map[string]any, error) {
   103  	factoryModel, exist := value.(factory.Model)
   104  	if !exist {
   105  		return nil, fmt.Errorf("%s does not find factory method", reflect.TypeOf(value).String())
   106  	}
   107  
   108  	definition := factoryModel.Factory().Definition()
   109  	if len(attributes) > 0 {
   110  		for key, value := range attributes[0] {
   111  			definition[key] = value
   112  		}
   113  	}
   114  
   115  	return definition, nil
   116  }
   117  
   118  // newInstance create a new factory instance.
   119  func (f *FactoryImpl) newInstance(attributes ...map[string]any) ormcontract.Factory {
   120  	instance := &FactoryImpl{
   121  		count: f.count,
   122  		query: f.query,
   123  	}
   124  
   125  	if len(attributes) > 0 {
   126  		attr := attributes[0]
   127  		if count, ok := attr["count"].(int); ok {
   128  			instance.count = &count
   129  		}
   130  	}
   131  
   132  	return instance
   133  }